WPF Tab_SelectionChanged Event - c#

I want to lock my wpf tab to change the index but I'm getting dispatcher error messages with my code below. Where am I doing wrong? I'm aware that once the content changes, it fires the same event but is there any other event to fire for this ?
private void MainTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!(e.Source is TabControl))
return;
if (Helper.GetProperty<bool>("IsTabLocked")) // my condition
{
MessageBox.Show("tab is locked");
e.Handled = true;
return;
}

The easiest solution that I can see is setting the required tab as the selected one when the execution comes to SelectionChanged event.
Try something like below.
int MyPreferedTabPageIndex = 1; // ?
private void MainTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (Helper.GetProperty<bool>("IsTabLocked")) // my condition
{
MainTabControl.SelectedIndex = MyPreferedTabPageIndex ;
MessageBox.Show("tab is locked");
}
}

I could come up with a custom solution with the source code below, but I'm sure someone has thought about this and there is an event or an easier trick which I've never heard before.
static int TabControlIndex = 0;
private void MainTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!(e.Source is TabControl))
return;
if (TabControlIndex == MainTabControl.SelectedIndex)
return;
if (Helper.GetProperty<bool>("IsTabLocked") && TabControlIndex != MainTabControl.SelectedIndex)
{
MessageBox.Show("locked");
MainTabControl.SelectedIndex = TabControlIndex;
// = true;
return;
}

Related

Check if tabControl1 is null?

How can i check if tabcontrol1 is null(no pages-tabs inside)??
I want this code for setting up my tab control when is null to visible=false;
and when its not null to visible=true;
I'm using this code in selection changed but nothing is happened.
private void TabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (TabControl==null)
{
TabControl.Visible = false;
}
else
{
TabControl.Visible = true;
}
But nothing it doesnt work.
I found this way which it works. But please check if it is the right way.
private void TabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (TabControl.SelectedTab == TabControl.TabPages[""])//
{
TabControl.Visible = false;
}
else
{
TabControl.Visible = true;
}
}
What you need to is to check the TabPages property or the TabCount of the TabControl:
this.tabControl.Visible = !this.tabControl.TabCount == 0;
This code will set the Visible property to false if there are no tab pages.

C# Infinite checkbox loop

I have an infinite loop caused by what I believe is an event being raised. However I don't understand why the event would be raised.
There is a listbox of items and another checkbox list that accompanies it. The checkboxlist shows child items from each item in the listbox.
private void checkedListBoxSignIn_ItemCheck(object sender, ItemCheckEventArgs e)
{
int userIndex = listBoxSignIn.SelectedIndex;
UpdateUserList();
listBoxSignIn.SelectedIndex = userIndex;
}
private void UpdateUserList()
{
listBoxSignIn.Items.Clear();
foreach (User u in _userList)
{
listBoxSignIn.Items.Add(u.Name);
}
}
private void listBoxSignIn_SelectedIndexChanged(object sender, EventArgs e)
{
int index = listBoxSignIn.SelectedIndex;
UpdateCheckListBox(index);
}
private void UpdateCheckListBox(int index)
{
checkedListBoxSignIn.Items.Clear();
foreach (Item i in _userList.Items)
{
checkedListBoxKartSignIn.Items.Add(i, i.status);
}
}
Once you tick a checkbox a loop starts:
-> checkedListBox_ItemCheck()
-> listbox_SelectedIndexChanged()
-> checkedListBox_ItemCheck()
...
It seems that adding an item to the checked listbox counts as a "Item Check" despite the "Item Check" occuring on construction.
Is this correct behaviour? If so, how do I avoid the loop?
I am using Winforms
When dealing with controls events that will load or modify other controls I usually had a flag to stop infinite loops like the one you might be having.
It is not an ideal solution but it was the simplest I could think at the time.
I haven't run the code below, just is just as illustration:
New variables:
private bool _updatingCheckList = false;
private bool _updatingList = false;
Then:
private void checkedListBoxSignIn_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (_updatingList) return; //if it is already doing it, don't do it again
int userIndex = listBoxSignIn.SelectedIndex;
try {
_updatingList = true;
UpdateUserList();
} finally {
_updatingList = false;
}
listBoxSignIn.SelectedIndex = userIndex;
}
private void UpdateUserList()
{
listBoxSignIn.Items.Clear();
foreach (User u in _userList)
{
listBoxSignIn.Items.Add(u.Name);
}
}
private void listBoxSignIn_SelectedIndexChanged(object sender, EventArgs e)
{
if (_updatingCheckList) return; //if it is already doing it, don't do it again
int index = listBoxSignIn.SelectedIndex;
try {
_updatingCheckList = true;
UpdateCheckListBox(index);
finally { _updatingCheckList = false; }
}
private void UpdateCheckListBox(int index)
{
checkedListBoxSignIn.Items.Clear();
foreach (Item i in _userList.Items)
{
checkedListBoxKartSignIn.Items.Add(i, i.status);
}
}
Another alternative is to remove the handlers before doing the operation and readding.
I hope this helps.
I have managed to fix the issue. The main problem here was that the listbox was being updated everytime anything was done to the checkedbox list. The fix was to ensure that some user interaction has actually taken place. The easiest way for me to do this was a simple if statement checking that both lists have been selected.
private void checkedListBoxSignIn_ItemCheck(object sender, ItemCheckEventArgs e)
{
if(listbox.SelectedIndex > -1 && checkedListBox.SelectedIndex > -1){
int userIndex = listBoxSignIn.SelectedIndex;
UpdateUserList();
listBoxSignIn.SelectedIndex = userIndex;
}
}

How to detect DataGridView CheckBox event change?

I have a winforms app and want to trigger some code when a checkbox embedded in a DataGridView control is checked / unchecked. Every event I have tried either
Triggers as soon as the CheckBox is clicked but before its checked state changes, or
Triggers only once the CheckBox looses its focus
I can't seem to find event that triggers immediately after the checked state changes.
Edit:
What I am trying to achieve is that when the checked state of a CheckBox in one DataGridView changes, the data in two other DataGridViews changes. Yet all the events I have used, the data in the other grids only changes after the CheckBox in the first DataGridView looses focus.
To handle the DatGridViews CheckedChanged event you must first get the CellContentClick to fire (which does not have the CheckBoxes current state!) then call CommitEdit. This will in turn fire the CellValueChanged event which you can use to do your work. This is an oversight by Microsoft. Do some thing like the following...
private void dataGridViewSites_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
dataGridViewSites.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
/// <summary>
/// Works with the above.
/// </summary>
private void dataGridViewSites_CellValueChanged(object sender,
DataGridViewCellEventArgs e)
{
UpdateDataGridViewSite();
}
P.S. Check this article https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx
I found #Killercam's solution to work but was a bit dodgy if the user double clicked too fast. Not sure if other's found that the case either. I found a another solution here.
It uses the datagrid's CellValueChanged and CellMouseUp. Changhong explains that
"The reason for that is OnCellvalueChanged event won’t fire until the DataGridView thinks you have completed editing. This makes senses for a TextBox Column, as OnCellvalueChanged wouldn’t [bother] to fire for each key strike, but it doesn’t [make sense] for a CheckBox."
Here it is in action from his example:
private void myDataGrid_OnCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
// Handle checkbox state change here
}
}
And the code to tell the checkbox it is done editing when it is clicked, instead of waiting till the user leaves the field:
private void myDataGrid_OnCellMouseUp(object sender,DataGridViewCellMouseEventArgs e)
{
// End of edition on each click on column of checkbox
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
myDataGrid.EndEdit();
}
}
Edit: A DoubleClick event is treated separate from a MouseUp event. If a DoubleClick event is detected, the application will ignore the first MouseUp event entirely. This logic needs to be added to the CellDoubleClick event in addition to the MouseUp event:
private void myDataGrid_OnCellDoubleClick(object sender,DataGridViewCellEventArgs e)
{
// End of edition on each click on column of checkbox
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
myDataGrid.EndEdit();
}
}
jsturtevants's solution worked great. However, I opted to do the processing in the EndEdit event. I prefer this approach (in my application) because, unlike the CellValueChanged event, the EndEdit event does not fire while you are populating the grid.
Here is my code (part of which is stolen from jsturtevant:
private void gridCategories_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
{
//do some stuff
}
}
private void gridCategories_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
{
gridCategories.EndEdit();
}
}
Here is some code:
private void dgvStandingOrder_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (dgvStandingOrder.Columns[e.ColumnIndex].Name == "IsSelected" && dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
{
bool isChecked = (bool)dgvStandingOrder[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
if (isChecked == false)
{
dgvStandingOrder.Rows[e.RowIndex].Cells["Status"].Value = "";
}
dgvStandingOrder.EndEdit();
}
}
private void dgvStandingOrder_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
{
dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
following Killercam'answer, My code
private void dgvProducts_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dgvProducts.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
and :
private void dgvProducts_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (dgvProducts.DataSource != null)
{
if (dgvProducts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString() == "True")
{
//do something
}
else
{
//do something
}
}
}
This also handles the keyboard activation.
private void dgvApps_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if(dgvApps.CurrentCell.GetType() == typeof(DataGridViewCheckBoxCell))
{
if (dgvApps.CurrentCell.IsInEditMode)
{
if (dgvApps.IsCurrentCellDirty)
{
dgvApps.EndEdit();
}
}
}
}
private void dgvApps_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// handle value changed.....
}
Ben Voigt found the best solution in a comment-reply above:
private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
Seriously, that's ALL you need.
What worked for me was CurrentCellDirtyStateChanged in combination with datagridView1.EndEdit()
private void dataGridView1_CurrentCellDirtyStateChanged( object sender, EventArgs e ) {
if ( dataGridView1.CurrentCell is DataGridViewCheckBoxCell ) {
DataGridViewCheckBoxCell cb = (DataGridViewCheckBoxCell)dataGridView1.CurrentCell;
if ( (byte)cb.Value == 1 ) {
dataGridView1.CurrentRow.Cells["time_loadedCol"].Value = DateTime.Now.ToString();
}
}
dataGridView1.EndEdit();
}
It's all about editing the cell, the problem that is the cell didn't edited actually, so you need to save The changes of the cell or the row to get the event when you click the check box so you can use this function:
datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)
with this you can use it even with a different event.
I have found a simpler answer to this problem. I simply use reverse logic. The code is in VB but it is not much different than C#.
Private Sub DataGridView1_CellContentClick(sender As Object, e As
DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
Dim _ColumnIndex As Integer = e.ColumnIndex
Dim _RowIndex As Integer = e.RowIndex
'Uses reverse logic for current cell because checkbox checked occures
'after click
'If you know current state is False then logic dictates that a click
'event will set it true
'With these 2 check boxes only one can be true while both can be off
If DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False And
DataGridView1.Rows(_RowIndex).Cells("Column3").Value = True Then
DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False
End If
If DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False And
DataGridView1.Rows(_RowIndex).Cells("Column2").Value = True Then
DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False
End If
End Sub
One of the best things about this is no need for multiple events.
I've tried some answers from here, but I've always had some kind of problem (like double clicking or using the keyboard). So, I combined some of them and got a consistent behavior (it's not perfect, but works properly).
void gridView_CellContentClick(object sender, DataGridViewCellEventArgs e) {
if(gridView.CurrentCell.GetType() != typeof(DataGridViewCheckBoxCell))
return;
if(!gridView.CurrentCell.IsInEditMode)
return;
if(!gridView.IsCurrentCellDirty)
return;
gridView.EndEdit();
}
void gridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e) {
if(e.ColumnIndex == gridView.Columns["cFlag"].Index && e.RowIndex >= 0)
gridView.EndEdit();
}
void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if(e.ColumnIndex != gridView.Columns["cFlag"].Index || e.RowIndex < 0)
return;
// Do your stuff here.
}
The Code will loop in DataGridView and Will check if CheckBox Column is Checked
private void dgv1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
dgv1.CommitEdit(DataGridViewDataErrorContexts.Commit);
var i = 0;
foreach (DataGridViewRow row in dgv1.Rows)
{
if (Convert.ToBoolean(row.Cells[0].Value))
{
i++;
}
}
//Enable Button1 if Checkbox is Checked
if (i > 0)
{
Button1.Enabled = true;
}
else
{
Button1.Enabled = false;
}
}
}
In the event CellContentClick you can use this strategy:
private void myDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 2)//set your checkbox column index instead of 2
{ //When you check
if (Convert.ToBoolean(myDataGrid.Rows[e.RowIndex].Cells[2].EditedFormattedValue) == true)
{
//EXAMPLE OF OTHER CODE
myDataGrid.Rows[e.RowIndex].Cells[5].Value = DateTime.Now.ToShortDateString();
//SET BY CODE THE CHECK BOX
myDataGrid.Rows[e.RowIndex].Cells[2].Value = 1;
}
else //When you decheck
{
myDataGrid.Rows[e.RowIndex].Cells[5].Value = String.Empty;
//SET BY CODE THE CHECK BOX
myDataGrid.Rows[e.RowIndex].Cells[2].Value = 0;
}
}
}
The best way that I found (which also doesn't use multiple events) is by handling the CurrentCellDirtyStateChanged event.
private void dataGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dataGridMatten.CurrentCell.OwningColumn == dataGridMatten.Columns["checkBoxColumn"] && dataGridMatten.IsCurrentCellDirty)
{
dataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
//your code goes here
}
}
To do this when using the devexpress xtragrid, it is necessary to handle the EditValueChanged event of a corresponding repository item as described here. It is also important to call the gridView1.PostEditor() method to ensure the changed value has been posted. Here is an implementation:
private void RepositoryItemCheckEdit1_EditValueChanged(object sender, System.EventArgs e)
{
gridView3.PostEditor();
var isNoneOfTheAboveChecked = false;
for (int i = 0; i < gridView3.DataRowCount; i++)
{
if ((bool) (gridView3.GetRowCellValue(i, "NoneOfTheAbove")) && (bool) (gridView3.GetRowCellValue(i, "Answer")))
{
isNoneOfTheAboveChecked = true;
break;
}
}
if (isNoneOfTheAboveChecked)
{
for (int i = 0; i < gridView3.DataRowCount; i++)
{
if (!((bool)(gridView3.GetRowCellValue(i, "NoneOfTheAbove"))))
{
gridView3.SetRowCellValue(i, "Answer", false);
}
}
}
}
Note that because the xtragrid doesnt provide an enumerator it is necessary to use a for loop to iterate over rows.
Removing the focus after the cell value changes allow the values to update in the DataGridView. Remove the focus by setting the CurrentCell to null.
private void DataGridView1OnCellValueChanged(object sender, DataGridViewCellEventArgs dataGridViewCellEventArgs)
{
// Remove focus
dataGridView1.CurrentCell = null;
// Put in updates
Update();
}
private void DataGridView1OnCurrentCellDirtyStateChanged(object sender, EventArgs eventArgs)
{
if (dataGridView1.IsCurrentCellDirty)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
You can force the cell to commit the value as soon as you click the checkbox and then catch the CellValueChanged event. The CurrentCellDirtyStateChanged fires as soon as you click the checkbox.
The following code works for me:
private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
SendKeys.Send("{tab}");
}
You can then insert your code in the CellValueChanged event.
I use DataGridView with VirtualMode=true and only this option worked for me
(when both the mouse and the space bar are working, including repeated space clicks):
private void doublesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var data_grid = (DataGridView)sender;
if (data_grid.CurrentCell.IsInEditMode && data_grid.IsCurrentCellDirty) {
data_grid.EndEdit();
}
}
private void doublesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == CHECKED_COLUMN_NUM && e.RowIndex >= 0 && e.RowIndex < view_objects.Count) { // view_objects - pseudocode
view_objects[e.RowIndex].marked = !view_objects[e.RowIndex].marked; // Invert the state of the displayed object
}
}
this worked for me
private void employeeDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == employeeDataGridView.Columns["employeeStatusColumn"].Index)
{
bool isChecked = (bool)employeeDataGridView.CurrentCell.Value;
if (isChecked)
{
MessageBox.Show("Checked " + isChecked); //out true;
}
else
{
MessageBox.Show("unChecked " + isChecked);
}
}
}
private void employeeDataGridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (employeeDataGridView.DataSource != null)
{
if (e.ColumnIndex == employeeDataGridView.Columns["employeeStatusColumn"].Index && e.RowIndex != -1)
{
employeeDataGridView.EndEdit();
}
}
}
private void dataGridViewPendingBill_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
bool isChecked = (bool) dataGridViewPendingBill[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
if (isChecked)
{
totalAmount += int.Parse(dataGridViewPendingBill.Rows[e.RowIndex].Cells["Amount"].Value.ToString());
textBoxAmount.Text = totalAmount.ToString();
}
else
{
totalAmount -= int.Parse(dataGridViewPendingBill.Rows[e.RowIndex].Cells["Amount"].Value.ToString());
textBoxAmount.Text = totalAmount.ToString();
}
dataGridViewPendingBill.EndEdit();
}

Timer, click, mousedown, mouseup events not working together

Looking for some help on a problem Im having
sorry if this question has already been asked, I can not find anything similar.
The idea is when a picturebox is clicked changed the image to ON.
If the picture box is held for more than 2 seconds to open a new form and leave the picturebox as OFF.
However if the picturebox is clicked ON and then held for 2 seconds and then returns i need the picturebox state to remain ON.
Here is what I have tried so far.
I believe for this to work correctly I need to stop MouseUp event from occuring.
Is there a way I can stop MouseUp when Tick occurs?
Is there a easier / better way to do this?
Any help would be appreciated.
private void time_HoldDownInternal_Tick(object sender, EventArgs e)
{
time_HoldDownInternal.Enabled = false;
time_HoldDownInternal.Interval = 1000;
form1show.Visible = true;
}
private void pb_pictureBoxTest_MouseDown(object sender, MouseEventArgs e)
{
mainMenuVariables.mousedown = true;
time_HoldDownInternal.Enabled = true;
}
private void pb_pictureBoxTest_MouseUp(object sender, MouseEventArgs e)
{
mainMenuVariables.mousedown = false;
//MessageBox.Show("mouse up");
time_HoldDownInternal.Enabled = false;
time_HoldDownInternal.Interval = 1000;
}
private void pb_pictureBoxTest_Click(object sender, EventArgs e)
{
if (mainMenuVariables.mousedown == true)
{
if (mainMenuVariables.pictureBox == false)
{
mainMenuVariables.pictureBox = true;
pb_pictureBoxTest.Image = new Bitmap(mainMenuVariables.pictureBoxOn);
return;
}
if (mainMenuVariables.pictureBox == true)
{
mainMenuVariables.pictureBox = false;
pb_pictureBoxTest.Image = new Bitmap(mainMenuVariables.pictureBoxOff);
return;
}
}
if (mainMenuVariables.mousedown == false)
{
//nothing
}
}
Rather than starting a timer, just record the current time on mouse down. Then in mouse up, check if it has been 2 seconds. e.g:
private void pb_pictureBoxTest_MouseDown(object sender, MouseEventArgs e)
{
mainMenuVariables.mousedown = true;
mainMenuVariables.mousedowntime = DateTime.Now;
}
private void pb_pictureBoxTest_MouseUp(object sender, MouseEventArgs e)
{
mainMenuVariables.mousedown = false;
var clickDuration = DateTime.Now - mainMenuVariables.mousedowntime;
if ( clickDuration > TimeSpan.FromSeconds(2))
{
// Do 'hold' logic (e.g. open dialog, etc)
}
else
{
// Do normal click logic (e.g. toggle 'On'/'Off' image)
}
}

Get Cell Control of GridView in DevExpress

I have a GridView that has a column with RepositoryItemCheckEdit as ColumnEdit. I want to disable this control for just one row. How can I do this? Any suggestions?
I have found a solution to the problem.
gridView1.CustomRowCellEditForEditing += OnCustomRowCellEditForEditing;
private void OnCustomRowCellEditForEditing(object sender, CustomRowCellEditEventArgs e)
{
if (e.Column.FieldName != "MyFieldName") return;
*code here*
e.RepositoryItem.ReadOnly = true;
}
you can make the editor read only by handling CustomRowCellEdit:
private void gridView1_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e)
{
if(code goes here)
e.RepositoryItem.ReadOnly = true;
}
you can also prevent the editor from being show by handling ShowingEditor:
private void gridView1_ShowingEditor(object sender, CancelEventArgs e)
{
if (code goes here)
e.Cancel = true;
}
in the class that inherits DataGridViewColum override method InitializeEditingControl
it has parameter rowIndex the write something like this
this.DataGridView.EditingControl.Enbale = rowIndex != 3; // or the number you need

Categories