Select Tab Page in TabControl without stealing focus - c#

Using TabControl.SelectTab("...") shows the tab but it also gives the tab focus. I would like to show a particular tab, but keep focus where it is.
I have data rows in a grid. Based on properties of the selected row, I show a different tab page to have a different UI layout. But when using arrow keys to scroll through rows, the focus switches to the selected tab -- which I don't want to happen.
Thanks.

You can try disabling the TabControl before setting the selected tab, then re-enabling it. This will prevent it from taking focus. I tested this on a tab control with a few controls on it, and didn't see any visual change, but you'll have to try it in your UI and see whether it's ok for you.
tabControl1.Enabled = false;
tabControl1.SelectTab("tabPage4");
tabControl1.Enabled = true;
To be safe, you could put the line to re-enable the TabControl in a finally block to make sure it doesn't get left disabled.

I don't think there's a built-in function, but you can do in this way:
private bool skipSelectionChanged = false;
private void dataGridView_SelectionChanged(object sender, EventArgs e)
{
if (skipSelectionChanged)
return;
// supposing we decide tab[0] has to be selected...
this.SelectTabWithoutFocus(this.tabControl1.TabPages[0]);
}
private void SelectTabWithoutFocus(TabPage tabPage)
{
this.skipSelectionChanged = true;
// "this" is the form in my case, so you get the current focused control
// (ActiveControl), backup it, and re-set it after Tab activation
var prevFocusedControl = this.ActiveControl;
if (this.ActiveControl != null)
{
this.tabControl1.SelectedTab = tabPage;
prevFocusedControl.Focus();
}
this.skipSelectionChanged = false;
}
Here, I backup the current focused control, select the desired tab, and finally set the focus to the original control.
Skipping boolean is necessary, because giving the focus to the grid you trigger SelectionChanged event again, causing infinite looping.

This selects the tab pages while keeping the focus on top, as asked here above:
tc.TabPages[0].Enabled = false;
tc.SelectTab(0);
tc.TabPages[0].Enabled = true;
tc is here my instance for the TabControl type (i. e. it IS my tab control, and it has a few "tab pages"). This works properly for me. My purpose is to loop through these tab pages with the Left and Right keys (arrows) i. e. when I go forwards (by Key.Right) and reach the last tabpage I want to have my focus on [0] without activating the DataGridView which I have in that page, and when I go backwards (by Key.Left) and reach [0] I want to have [tc.TabCount - 1] enabled, which is the last one. The code for this case is:
tc.TabPages[tc.TabCount - 1].Enabled = false;
tc.SelectTab(tc.TabCount - 1);
tc.TabPages[tc.TabCount - 1].Enabled = true;
The complete piece of code is:
private bool KeyTc(System.Windows.Forms.Keys keyData)
{
if (keyData == K.Left && tc.SelectedIndex == 0)
{
tc.TabPages[tc.TabCount - 1].Enabled = false;
tc.SelectTab(tc.TabCount - 1);
tc.TabPages[tc.TabCount - 1].Enabled = true;
return true;
}
else if (keyData == K.Right && tc.SelectedIndex == tc.TabCount - 1)
{
tc.TabPages[0].Enabled = false;
tc.SelectTab(0);
tc.TabPages[0].Enabled = true;
return true;
}
return false;
}
This bool KeyTc is returned to a case in a switch statement for key evaluation in:
protected override bool ProcessCmdKey(ref Message keyMsg, Keys keyData)
{ switch keyData { ... } }

Base on the solution proposed by "Jeff Ogata : You can try disabling the TabControl before setting the selected tab, then re-enabling it. This will prevent it from taking focus", here bellow my solution:
tabMain.SelectedPageChanging += (s, e) =>
{
tabMain.Enabled = false;
};
tabMain.SelectedPageChanged += (s, e) =>
{
tabMain.Enabled = true;
};
Note: this code is using DevExpress "DevExpress.XtraTab.XtraTabControl".

Related

Disabling a checkbox when another checkbox is clicked

I am trying to disable the thin and crispy checkbox when traditional checkbox is clicked. I have these in a group due to me enabling the whole group when the numericUpDown value is set to 1. When I click traditional checkbox, it doesn't disable the thin and crispy checkbox. I am using windows form application
Code
private void NudQuantity1_ValueChanged(object sender, EventArgs e)
{
if (NudQuantity1.Value == 0)
{
gbCheesePizza.Enabled = false;
}
else
{
gbCheesePizza.Enabled = true;
}
if (CBXTraditional1.Checked == true)
{
CBXthinandcrispy1.Enabled = false;
}
}
When I run this code outside of a groupbox, it works perfectly.
I don't think this block should be inside the event handler
if (CBXTraditional1.Checked == true)
{
CBXthinandcrispy1.Enabled = false;
}
It means that, provided you've got no other event handling for the checkboxes, this code will only be executed when you change the value of NudQuantity1 so it won't execute anything when you click the checkboxes afterwards.
Try use radio buttons as Steve mentioned. They do this for you.

Xamarin C#: ImageButton, deselecting a button upon selecting another one

I'm building a game, one of the 'mechanics' is selecting a tool by clicking on one of the three ImageButtons (representing a Drill, a Hammer and a Brush).
I want to mark the clicked button selected but only until I switch tools by selecting a second one. Is there any clean way of unselecting/selecting buttons?
Current code:
if (drillbutton.Selected)
{
brushbutton.Selected = false;
hammerbutton.Selected = false;
}
else if(hammerbutton.Selected)
{
drillbutton.Selected = false;
brushbutton.Selected = false;
}
else if(brushbutton.Selected)
{
drillbutton.Selected = false;
hammerbutton.Selected = false;
}
There must be an easier way... Right?
You could assign the same click handler to every button, like this:
private void SelectButton(object sender, EventArgs args) {
// you will have to have an IEnumerable that contains your set of buttons
foreach(var b in buttons) {
if (b == (Button)selected) b.Selected = true else b.Selected = false;
}
}

C# Keys.Apps will always open Windows context menu

I'm trying to handle the Apps/Context Menu key on the keyboard. The key should be catched at a TextBox and then should show the programmed ContextMenuStrip of a DataGridView object.
However getting the ContextMenuStrip displayed turned out pretty simple. My only problem is that the flag e.Handled = true does not seem to work to prevent the Windows default context menu for the TextBox to appear. So it's opening the ContextMenuStrip for the DataGridView and the default context menu for the TextBox.
Following code applies:
void EditSearchField_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Apps)
{
// ContextMenuStrip is shown here
DataGridView1.ContextMenuStrip.Show(DataGridView1, new Point(0, 0));
e.Handled = true;
e.SuppressKeyPress = true;
}
}
The result looks rather unpleasant. KeyPreview = true is also set.
Any ideas?
Since ProcessCmdKey() and PreviewKeyDown() didn't do the job, I decided to take another approach...
I found a (at least for my needs) decent workaround for my problem. In the "designer" portion of my form i defined a new ContextMenuStrip for my TextBox:
// editSearchField
[...]
this.editSearchField.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();
This resulted in the Windows default context menu not showing anymore. Since the ContextMenuStrip has no ToolStripMenuItems it is discarded immediately.
For completeness, here's how I changed the KeyDown() function
void EditSearchField_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Apps)
{
if (dgvClients.SelectedRows.Count > 0)
{
// force the selected row to be visible, or else we could get a .NET debugger
dgvClients.CurrentCell = dgvClients.SelectedRows[0].Cells[0];
// prepare context menu (disable inaccessible entries)
Point ptMouse = dgvClients.GetCellDisplayRectangle(0, dgvClients.SelectedRows[0].Index, false).Location;
var mouseEvtArgs = new MouseEventArgs(MouseButtons.Right, 1, 0, 0, 0);
var mouseDgvArgs = new DataGridViewCellMouseEventArgs(0, dgvClients.SelectedRows[0].Index, ptMouse.X, ptMouse.Y, mouseEvtArgs);
DgvClientsMouseDown(dgvClients, mouseDgvArgs);
// calculate location for the context menu and finally show it
Point ptContextMenuPos = dgvClients.PointToScreen(ptMouse);
ptContextMenuPos.X += dgvClients.Width / 2;
ptContextMenuPos.Y += dgvClients.RowTemplate.Height / 2;
dgvClients.ContextMenuStrip.Show(ptContextMenuPos);
}
e.Handled = true;
e.SuppressKeyPress = true;
}
}
Edit: fixed a bug within the code

Is the following possible when initiating an if pictureBox clicked event?

I have been trying to make a matching game and I recently learned that the following is possible:
if (checkBox1.checked = true)
{
MessageBox.Show("For Example.")'
}
Then why is the following not possible?
private void pictureBox1_Click(object sender, EventArgs e)
{
MessageBox.Show("Now Pick Another Hidden Picture!");
pictureBox1.Visible = false;
if (pictureBox13.Click = true)
{
MessageBox.Show("Great!");
pictureBox13.Visible = false;
double score = score + 1;
textBox1.Text = (score).ToString();
}
else
{
MessageBox.Show("Try Again!");
pictureBox13.Visible = true;
pictureBox1.Visible = true;
}
}
There is a error line under .Click , and the error is :
The event 'system.Windows.Forms.Control.Click' can only appear on the left hand side of += or -=.
What does this mean? And why does this work for checking checkboxes, but not for clicking pictureboxes? Thank in advance.
Checked is the state of a checkbox. At any given time, a checkbox is either checked or unchecked. Reading myCheckBox.Checked immediately returns the current state of the checkbox.
Click is an event. What do you expect if (pictureBox.Click == true) to do? Tell you if the pictureBox has been clicked within the last X seconds? Wait X seconds for the user to click (or not click) on the pictureBox?
In other words: If you check a checkbox, it stays checked until it is unchecked. Thus, it makes sense to check the current state of the checkbox. On the other hand, if you click a button, it is "clicked" for only an instant and then returns to being "unclicked". Thus, it just does not make sense to query the "clicked" state of a button.
PS: Comparisons are done with ==, not with =. The latter is an assigment. And, as Hugh correctly points out in the comments, if (boolean) is enough, if (boolean == true) is redundant.

Programatically select rows in a grid in MouseDown gets reset during MouseUp/MouseClick

I've got a MouseDown event on my GridView:
private void gdcSVNDefaultView_MouseDown(object sender, MouseEventArgs e)
{
var vw = (GridView)sender;
var hitInfo = vw.CalcHitInfo(e.Location);
DXMouseEventArgs.GetMouseArgs(e).Handled = SelectChildRows(hitInfo.RowHandle, vw);
}
private static bool SelectChildRows(int r, GridView view)
{
if (!view.IsGroupRow(r) || !view.GetRowExpanded(r))
return false;
var childRowCount = view.GetChildRowCount(r);
var first = view.GetChildRowHandle(r, 0);
var last = (first + childRowCount - 1);
view.SelectRange(first, last);
return true;
}
Screenshot of my form since not everyone is familiar with the DevExpress grids and might not know what i mean by a 'Group':
When you have a Group in the grid and click on that Group row instead of an actual 'data' row, I want to select all the child rows belonging to that Group.
The code works. If I click on a Group (for example, Type: Warning in my screenshot) and hold the mouse down I can see it select all the child rows... But as soon as I let up on the mouse, it de-selects them and selects just the group row. So if you just click quickly, like you normally would, you see them all flash quickly as as their selected state toggles.
Unfortunately, the Mouse events don't have any sort of "Handled" property I can set to make the MouseUp / Click not fire. I tried moving the code in my MouseDown to the MouseUp event and it doesn't even temporarily select everything. DX also has a "RowClick" event, tried it there... same results as the MouseUp.
Any ideas on how I can "cancel" those events?
Edit: Turns out there is a Handled property if you cast the MouseEventArgs to a DXMouseEventArgs object... But it still observes the same behavior.
Alright, so I found out that I need to set Handled in both the MouseDown and MouseUp events in order to get the behavior desired:
public class MyForm
{
private bool _b;
private void gdcSVNDefaultView_MouseDown(object sender, MouseEventArgs e)
{
var vw = (GridView)sender;
var hitInfo = vw.CalcHitInfo(e.Location);
if (hitInfo.HitTest == GridHitTest.RowGroupButton)
return;
_b = false;
if (vw.IsGroupRow(hitInfo.RowHandle))
_b = SelectChildRows(hitInfo.RowHandle, vw);
DXMouseEventArgs.GetMouseArgs(e).Handled = _b;
}
private void gdcSVNDefaultView_MouseUp(object sender, MouseEventArgs e)
{
DXMouseEventArgs.GetMouseArgs(e).Handled = _b;
}
private static bool SelectChildRows(int r, GridView view)
{
if (!view.GetRowExpanded((r)))
return false;
if (ModifierKeys != Keys.Shift && ModifierKeys != Keys.Control)
view.ClearSelection();
var childRowCount = view.GetChildRowCount(r);
var first = view.GetChildRowHandle(r, 0);
var last = (first + childRowCount - 1);
view.SelectRange(first, last);
return true;
}
}
Note: This IF block is NOT required for the core functionality. I found that if I click on multiple Groups it was adding that Range to the current Selection, which might be unexpected behavior for the end user. I added a check to see if the user is holding down the Control or Shift keys when they click the Group row. If not, then clear the current selection and select the new range
Have you tried using the Click event instead of MouseDown? Might be worth a shot. From here:
Note that the Click event also fires after the mouse button has been released. The difference is that the Click event is only raised if the mouse pointer is within the View when releasing the mouse button.

Categories