Enable button by event handling - c#

Have a tab page with 2 panels, a data grid view and a 'clear' button.There are only textboxes in the panels and the grid is unbound. Data input is by user. The clear btn is disabled by default. My requirement is to enable it only if any of the textboxes is not empty or there is more than 1 row in the grid. This code isn't working. Please help.
public Form1()
{
InitializeComponent();
foreach (Control c in InvoiceTab.Controls)
{
if (c is DataGridView)
{
DataGridView dgv = c as DataGridView;
if (dgv.RowCount > 1)
{
EnableClearBtnBool = true;
btnClear.Enabled = true;
break;
}
else
{
EnableClearBtnBool = false;
btnClear.Enabled = false;
break;
}
}
}
foreach (Control c1 in panel1.Controls)
{
if (c1 is TextBox)
{
if (c1.Text != "")
{
EnableClearBtnBool = true;
c1.TextChanged -= EnableClearBtn;
c1.TextChanged += EnableClearBtn;
break;
}
else
EnableClearBtnBool = false;
}
}
foreach (Control c2 in panel2.Controls)
{
if (c2 is TextBox)
{
if (c2.Text != "")
{
EnableClearBtnBool = true;
c2.TextChanged -= EnableClearBtn;
c2.TextChanged += EnableClearBtn;
break;
}
else
EnableClearBtnBool = false;
}
}
}
bool EnableClearBtnBool = false;
private void EnableClearBtn(object sender, EventArgs e)
{
if (EnableClearBtnBool == true)
btnClear.Enabled = true;
else
btnClear.Enabled = false;
}

That code is almost certainly not working because of its location first and foremost. However, there are some fundamental changes we should be able to make as well. First we're going to need this code in a method that can be called frequently:
private void RefreshClearButton()
{
btnClear.Enabled = textBox1.Text.Length > 0 ||
textBox2.Text.Length > 0 || ...
dataGridView.RowCount > 1;
}
but, we also need to leverage the TextChanged event on all of the text boxes:
private void textBox_TextChanged(object sender, EventArgs e)
{
RefreshClearButton();
}
so you need to hook all of them up to this event handler. Now, we have two more events we need to consume, on the DataGridView, RowsAdded and RowsRemoved:
private void dataGridView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
RefreshClearButton();
}
private void dataGridView_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
RefreshClearButton();
}
and so now we're notified every time something changes. If you must iterate through the panels rather than naming every single text box along the way then you might want to do something like this:
private bool HasTextBeenEntered(ControlCollection controls)
{
foreach (var c in controls)
{
if (c is TextBox && ((TextBox)c).Text.Length > 0) { return true; }
else if (c is Control && HasTextBeenEntered(((Control)c).Controls)) { return true; }
}
return false;
}
which would change the RefreshClearButton method slightly:
private void RefreshClearButton()
{
btnClear.Enabled = HasTextBeenEntered(this.Controls) || dataGridView.RowCount > 1;
}
DISCLAIMER: none of this code is compiled so don't be surprised if you have to tweak it, but it will get you more than 90% of the way.

Related

Previous button not working for DataGridView C#

How can I fix the previous button to move backward in dataGridView row?
The next button is working.
It does nothing.
I don't know how to fix that. Any ideas? I would appreciate it.
Here is my code:
int nRow;
private void Form1_Load
{
nRow = DataGridView2.CurrentCell.RowIndex;
}
private void PreviousData_Click
{
if (row>=0)
{
if (row!=0)
{
DataGridView2.Rows[row].Selected = false;
DataGridView2.Rows[--row].Selected = true;
}
else
{
MessageBox.Show("Need More Data!");
}
}
}
int row;
private void DataGridView2_CellClick
{
if (DataGridView2.SelectedRows.Count != -1)
{
row = DataGridView2.CurrentRow.Index;
}
}
Bind the following two events to your previous and next buttons, respectively and it will do the job.
private void PreviousData_Click(object sender, EventArgs e)
{
var currentRow = DataGridView2.CurrentCell.RowIndex;
if (currentRow == 0)
return;
else
currentRow--;
DataGridView2.ClearSelection();
DataGridView2.CurrentCell = DataGridView2.Rows[currentRow].Cells[0];
DataGridView2.Rows[currentRow].Selected = true;
}
private void NextData_Click(object sender, EventArgs e)
{
var currentRow = DataGridView2.CurrentCell.RowIndex;
if (currentRow == DataGridView2.RowCount - 1)
return;
else
currentRow++;
DataGridView2.ClearSelection();
DataGridView2.CurrentCell = DataGridView2.Rows[currentRow].Cells[0];
DataGridView2.Rows[currentRow].Selected = true;
}

Windows Form->DataGridView->DataGridViewCheckBoxColumn Uncheck All Leaves One Item Checked

We have a very strange problem in a Windows Form that we cannot seem to figure out.
Our Windows Form has a DataGridView with a DataGridViewCheckBoxColumn in the first column.
We've added a the following functionality that allows a user to shift->click to select multiple rows in this grid:
int colHit = gvLibrary.HitTest(e.X, e.Y).ColumnIndex;
int lastRowHit;
//mouse left click
if (e.Button == MouseButtons.Left)
{
if (colHit == 0)
{
if (Control.ModifierKeys == Keys.Shift)
{
lastRowHit = gvLibrary.HitTest(e.X, e.Y).RowIndex;
ShiftClickCheckBoxSetter(this.gvLibrary, int.Parse(txtFirstClickRow.Text), lastRowHit);
}
else
{
int firstRowHit = gvLibrary.HitTest(e.X, e.Y).RowIndex;
txtFirstClickRow.Text = firstRowHit.ToString();
}
}
}
Here's the CheckBoxSetter Code:
private void ShiftClickCheckBoxSetter(DataGridView dataGridView, int p, int lastRowHit)
{
if (p < lastRowHit)
{
for (int i = p; i < lastRowHit; i++)
{
dataGridView.Rows[i].Cells[0].Value = true;
}
}
else//
{
for (int i = p; i >= lastRowHit; i--)
{
dataGridView.Rows[i].Cells[0].Value = true;
}
}
}
And this is working as expected.
We've also added a ContextMenuStrip to the control for a right-click event.
else if (e.Button == MouseButtons.Right)
{
if (colHit != 0)
{
ContextMenuStrip m = new ContextMenuStrip();
m.Items.Add("Select All", null, m_LibraryItemClicked);
m.Items.Add("Select None", null, m_LibraryItemClickedNone);
m.Show(gvLibrary, e.Location);
}
}
Delegate Event One:
void m_LibraryItemClicked(object sender, EventArgs e) {
foreach (DataGridViewRow dgvr in gvLibrary.Rows)
{
if (dgvr.Selected) {
dgvr.Selected = false;
}
dgvr.Cells["LSelect"].Value = true;
}
}
Delegate Event Two:
private void m_LibraryItemClickedNone(object sender, EventArgs e)
{
foreach (DataGridViewRow dgvr in gvLibrary.Rows)
{
if (dgvr.Selected)
dgvr.Selected = false;
dgvr.Cells["LSelect"].Value = false;
}
}
This allows to the user to select all or select none for the checkboxes.
When the Select All selection is chosen, all check boxes are checked:
However when the Select None option is selected:
All check boxes are de-selected, except for the last one checked in the Shift-Click event:
I would think that iterating through all of the Grid Rows and setting the checkbox to not selected would suffice, IE:
private void m_LibraryItemClickedNone(object sender, EventArgs e)
{
foreach (DataGridViewRow dgvr in gvLibrary.Rows)
{
if (dgvr.Selected)
dgvr.Selected = false;
dgvr.Cells["LSelect"].Value = false;
}
}
However there seems to be some kind of state property that is disallowing this checkbox in that row to be changed.
Thanks in advance.
I checked your code and could reproduce this behaviour. The problem seem to be with the current cell (not the cell selected). When you try to change this particular cell, the action doesn't get executed immediately.
To change this behaviour add a dataGridView1.CurrentCell = null; before changing the value of the "LSelect" cell. This should fix your issue.
private void m_LibraryItemClickedNone(object sender, EventArgs e)
{
dataGridView1.CurrentCell = null;
foreach (DataGridViewRow dgvr in gvLibrary.Rows)
{
if (dgvr.Selected)
dgvr.Selected = false;
dgvr.Cells["LSelect"].Value = false;
}
}

Add an OnLeave function to 140 objects?

I got a windows form with around 140 numericUpDown elements and want all of them to do this:
private void numericUpDown_B1_RS_LS_Leave(object sender, EventArgs e)
{
if (numericUpDown_B1_RS_LS.Value < 1 || numericUpDown_B1_RS_LS.Value > 6)
{
numericUpDown_B1_RS_LS.BackColor = Color.Red;
}
else
{
numericUpDown_B1_RS_LS.BackColor = Color.White;
}
}
Is there a more comfortable way of doing this than just adding 140 functions to the form manually?
Assuming you want the same method for all NUDs on your form this will do it:
public Form1()
{
InitializeComponent();
foreach (Control ctl in this.Controls)
if (ctl.GetType() == typeof(NumericUpDown) ) ctl.Leave += numericUpDown_Leave;
}
private void numericUpDown_Leave(object sender, EventArgs e)
{
NumericUpDown NumericUD = (NumericUpDown) sender ;
if (NumericUD.Value < 1 || NumericUD.Value > 6)
{
NumericUD.BackColor = Color.Red;
}
else
{
NumericUD.BackColor = Color.White;
}
}
You can just as easily iterate over the Controls collection of another container, say a Panel or a GroupBox if the NUDs are not directly sitting on the Form. And if some NUDs should be excepted from that behaviour you could mark them in some way, maybe in their Tag or Name and check for that before adding their handlers ..
You should not write multiple functions for same code. Instead you can create just one function and assign it to the event method of your numericUpDown element i.e. "UpDown" event.
So whenever any of the element's "UpDown" event will fire, your same function will be executed. And as far as you want to write different methods for different elements, then consider that in your method
Method(object sender, EventArguments e)
sender is the actual sender object of your updownElement type, and that code will be executed for that particular object only.
You can take for loop to iterate your all 140 elements and assign this function to the "UpDown" element.
maybe you could write a static method to handle the adding of event handler recursively, something like:
private void frmMain_Load(object sender, EventArgs e)
{
AddHandleNumericUpdownLeave(this);
}
public static void AddHandleNumericUpdownLeave(Control theContrl)
{
if (theContrl.Controls != null && theContrl.Controls.Count > 0)
{
foreach (Control cControl in theContrl.Controls)
{
AddHandleNumericUpdownLeave(cControl);
}
}
else
{
NumericUpDown nudCtrl = theContrl as NumericUpDown;
if (nudCtrl != null)
{
nudCtrl.Leave += (object senderT, EventArgs eT) =>
{
var tmpCtrl = senderT as NumericUpDown;
if (tmpCtrl != null)
{
if (tmpCtrl.Value < 1 || tmpCtrl.Value > 6)
{
tmpCtrl.BackColor = Color.Red;
}
else
{
tmpCtrl.BackColor = Color.White;
}
}
}
}
}
}
but this might be costly if you have too many controls in a form...

Hiding and showing panels

EDIT
I've found and posted the solution.
I am trying to make an installer for my application and I am trying to do that with panels (I don't know if this is a good way of doing it, but this gives me more customization options instead of using the install shield program). What would be the best way to do this?
This is the code I have right know:
C# Code
foreach (var c in Controls)
{
if (!(c is Panel)) continue;
if (c.Name == "pnlBottom") continue;
c.Visible = c.Name.Contains(_currentPanel.ToString());
if (c.Visible) return;
}
Try this, it changes the Visibility of a single Panel:
private void PanelVisible(string panelName, bool visible)
{
var panel = this.Controls.OfType<Panel>().FirstOrDefault(p => p.Name == panelName);
if (panel != default(Panel)) panel.Visible = visible;
}
If you want to make all Invisible, but one:
private void PanelVisible(string panelName)
{
foreach(var panel in this.Controls.OfType<Panel>().Where(p=>p.Name!="pnlBottom"))
{
panel.Visible = panel.Name == panelName;
}
}
This is the code I use to show a panel:
foreach (var c in Controls)
{
if (!(c is Panel)) continue;
if (c.Name == "pnlBottom") continue;
c.Visible = c.Name.Contains(_currentPanel.ToString());
if (c.Visible) return;
}
And this is the code I use to call it and browse through my panels:
Next button
private void btnNext_Click(object sender, EventArgs e)
{
if (pnlContent1.Visible) { ShowPanel("2"); return; }
if (pnlContent2.Visible) { ShowPanel("3"); return; }
if (pnlContent3.Visible) { ShowPanel("4"); return; }
if (pnlContent4.Visible) { ShowPanel("5"); return; }
}
Back button
private void btnBack_Click(object sender, EventArgs e)
{
if (pnlContent2.Visible) { ShowPanel("1"); return; }
if (pnlContent3.Visible) { ShowPanel("2"); return; }
if (pnlContent4.Visible) { ShowPanel("3"); return; }
if (pnlContent5.Visible) { ShowPanel("4"); return; }
}
I hope this will be use to someone else as well :D!

What algorithm is used in this code?

This code checks and unchecks the child nodes of a treeview control.
What algorithm is used in this code?
private int _callCountUp;
private int _callCountDn;
private void tvwPermissions_AfterCheck(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
bool anyChecked = false;
if (_callCountDn == 0 && e.Node.Parent != null)
{
anyChecked = false;
foreach (TreeNode childNode in e.Node.Parent.Nodes)
{
if (childNode.Checked)
{
anyChecked = true;
break;
}
}
_callCountUp += 1;
if (anyChecked)
e.Node.Parent.Checked = true;
_callCountUp -= 1;
}
if (_callCountUp == 0)
{
foreach (TreeNode childNode in e.Node.Nodes)
{
_callCountDn += 1;
childNode.Checked = e.Node.Checked;
_callCountDn -= 1;
}
}
}
Not so sure this has a name. It is quite standard, the _callCountUp/Dn fields avoid trouble when changing the Checked property of a node causes the AfterCheck event handler to run again. StackOverflow is a very typical outcome when the event handler recurses without bound.
The generic pattern resembles this:
private bool modifyingNodes;
private void treeview_AfterCheck(object sender, TreeViewEventArgs e) {
if (modifyingNodes) return;
modifyingNodes = true;
try {
// etc..
}
finally {
modifyingNodes = false;
}
}
The finally block ensures that a handled exception (such as through ThreadExceptionDialog) doesn't permanently leave the state variable set to true. It's optional of course.

Categories

Resources