Using MS Visual Studio 2012, Telerik, C#.ASP.NET.
The logic I need is as follows:
If a columns data on all rows is null then hide the column
Basically if a column has 3 rows of data, if there all null then dont bother displaying that column, however if there is a value in 1 of them, then show the column.
been playing around:
foreach (GridColumn columns in dgvUserResults.Columns)
{
if (columns != null)
{
columns.Visible = false;
}
else
{
columns.Visible = true;
}
}
code doesn't work of course doesnt iterate through the foreach loop just skips it. Although not bothered about that even if it did iterate through I need a way to check if all column[name] rows are null. There a nice Telerik one liner?
Please try with below code snippet.
Using UniqueName
protected void RadGrid1_PreRender(object sender, EventArgs e)
{
foreach (GridColumn column in RadGrid1.MasterTableView.Columns)
{
// If you used ClientSelectColumn then below code is not worked For that you have to put condition
//if(column.ColumnType == "GridBoundColumn")
int TotalNullRecords = (from item in RadGrid1.MasterTableView.Items.Cast<GridDataItem>()
where string.IsNullOrWhiteSpace(item[column.UniqueName].Text) ||
item[column.UniqueName].Text == " "
select item).ToList().Count;
if (TotalNullRecords == RadGrid1.MasterTableView.Items.Count)
{
RadGrid1.MasterTableView.Columns.FindByUniqueName(column.UniqueName).Visible = false;
}
}
}
By Using Index
protected void RadGrid1_PreRender(object sender, EventArgs e)
{
foreach (GridColumn column in RadGrid1.MasterTableView.Columns)
{
// If you used ClientSelectColumn then below code is not worked For that you have to put condition
//if(column.ColumnType == "GridBoundColumn")
int TotalNullRecords = (from item in RadGrid1.MasterTableView.Items.Cast<GridDataItem>()
where string.IsNullOrWhiteSpace(item[column.UniqueName].Text) ||
item[column.UniqueName].Text == " "
select item).ToList().Count;
if (TotalNullRecords == RadGrid1.MasterTableView.Items.Count)
{
column.Visible = false;
}
}
}
For col = 0 To myRadGridView.ColumnCount
Dim mustKeepColumn As Boolean = False
For Each r In myRadGridView.Rows
If Not String.IsNullOrEmpty(r.Cells(col).Value.ToString) Then
mustKeepColumn = True
Exit For
End If
Next
If Not mustKeepColumn Then
myRadGridView.Columns(col).IsVisible = False
End If
Next
Related
I have a datagridview and a checkbox column attached to it. If the user checks a few rows and then presses a button, I would like to be able to get a certain cell value from each row where the box was ticked.
Something maybe like this:
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (Convert.ToBoolean(row.Cells[CheckBoxColumn1.Name].Value) == true)
{
//...
}
}
The problem is that the datagridview might contain up to 3000 or 4000 rows. I would like to see if there is a faster way to get the checked rows, other than to iterate through all the rows for the grid.
If you don't want to iterate all rows, then use temporary list of checked rows.
Then after button was clicked use values from that List
HashSet<DataGridViewRow> _CheckedRows = new HashSet<DataGridViewRow>();
private void DataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (DataGridView.Columns[e.ColumnIndex].Name.Equals(CheckBoxColumn1.Name) == false)
return;
DataGridViewRow row = DataGridView.Rows[e.RowIndex];
if (Convert.ToBoolean(row.Cells[CheckBoxColumn1.Name].Value) == true)
{
_CheckedRows.Add(row);
}
else
{
_CheckedRows.Remove(row);
}
}
You could manage your own list of checked rows.
You would bind to the dataGridView1.CellClick event, and add/remove rows from the list:
var checkedRows = new List<DataGridViewRow>();
dataGridView1.CellClick += (sender, args) =>
{
if (args.RowIndex != YOUR_CHECKBOX_COLUMN_INDEX)
{
return;
}
var cell = dataGridView1[args.ColumnIndex, args.RowIndex];
if (cell.Value == null)
{
cell.Value = false;
}
cell.Value = !(bool)cell.Value;
if ((bool)cell.Value)
{
checkedRows.Add(dataGridView1.Rows[args.RowIndex]);
}
else
{
checkedRows.Remove(dataGridView1.Rows[args.RowIndex]);
}
};
All you have to do then is:
foreach (DataGridViewRow row in checkedRows)
{
//...
}
You can use Linq like this :
var checkedRows = from DataGridViewRow r in dataGridView1.Rows
where Convert.ToBoolean(r.Cells[CheckBoxColumn1.Name].Value) == true
select r;
foreach (var row in checkedRows)
{
//
}
Using CheckBoxColumn1.Name instead of CheckBoxColumn1.Index seems like a tiny bottleneck to me.
To avoid the casting to DataGridViewRow and Boolean, my suggestion is something like (not tested):
int colIndex = CheckBoxColumn1.Index; // or dataGridView1.Columns.IndexOf(CheckBoxColumn1.Name) ?
for ( int r = 0; r < dataGridView1.RowCount; r++ )
{
if ( true.Equals( dataGridView1[colIndex, r].Value ) )
{
//...
}
}
The other answers that use cell events are better because the list of checked rows will be ready when needed, but also can be a bit harder to maintain/debug depending on how you do the filtering and rest. Here is my version:
private HashSet<int> checkedRowIndexes = new HashSet<int>();
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if ( e.ColumnIndex == CheckBoxColumn1.Index )
{
if ( true.Equals( dataGridView1[CheckBoxColumn1.Index, e.RowIndex].Value ) )
checkedRowIndexes.Add(e.RowIndex);
else
checkedRowIndexes.Remove(e.RowIndex);
}
}
I have a datagridview named PTable which shows a table from database. I also have a button that functions to copy the data from selected rows to the textboxes I have.
This code that I have right now copies two selected rows from the datagridview but when I only select one row or when I select more than 2 rows, it says that "Index was out of range"
What should happen is that it should copy any number of rows and not only 2 rows
private void button11_Click(object sender, EventArgs e)
{
productid.Text = PTable.SelectedRows[0].Cells[0].Value + string.Empty;
productname.Text = PTable.SelectedRows[0].Cells[1].Value + string.Empty;
unitprice.Text = PTable.SelectedRows[0].Cells[4].Value + string.Empty;
productid2.Text = PTable.SelectedRows[1].Cells[0].Value + string.Empty;
productname2.Text = PTable.SelectedRows[1].Cells[1].Value + string.Empty;
unitprice2.Text = PTable.SelectedRows[1].Cells[4].Value + string.Empty;
}
If a user have not selected two rows, your index 1 (PTable.SelectedRows[1]) is not valid, because the item is not there.
Before you can run this code, you have to check, if the user selected two rows:
if (PTable.SelectedRows.Count == 2)
{
//Your code ...
}
else
{
//Not two rows selected
}
First, make sure SelectionMode of DataGridView to FullRowSelect(Hope you have done it already)
Second, Use foreach or any looping logic to go through each selected rows.
Third, It's always better to write modular code. Write a validation method which returns TRUE or FALSE based on selection of the grid rows.
Now based on returned value you need to continue writing your business logic.
Fourth, Make sure to use NULL checks
So let's start by re-factoring the code.
private void button11_Click(object sender, EventArgs e)
{
If(IsRowOrCellSelected())
{
//loop through selected rows and pick your values.
}
}
I am just writing sample, make sure PTable is accessible.
private boolean IsRowOrCellSelected()
{
if (PTable.SelectedRows.Count > 0)
{
DataGridViewRow currentRow = PTable.SelectedRows[0];
if (currentRow.Cells.Count > 0)
{
bool rowIsEmpty = true;
foreach(DataGridViewCell cell in currentRow.Cells)
{
if(cell.Value != null)
{
rowIsEmpty = false;
break;
}
}
}
if(rowIsEmpty)
return false;
else
return true;
}
Code can be still improved.
To work with varying number of selected rows I suggest the following.
My approach is:
There may be more than 5 rows, that You have to display. First create 3 panels for the TextBoxes. I have named the one for the productids as here. Generate the textboxes from code, this way it's easier to maintain more than 5 selected rows:
// This code goes to the constructor of the class. Not in the button click
List<TextBox> productids = new List<TextBox>();
List<TextBox> productnames = new List<TextBox>();
List<TextBox> unitprices = new List<TextBox>();
for (int i = 0; i < 5; i++)
{
productids.Add(new TextBox { Top = i * 32 });
productnames.Add(new TextBox { Top = i * 32 });
unitprices.Add(new TextBox { Top = i * 32 });
here.Controls.Add(productids[i]);
here2.Controls.Add(productnames[i]);
here3.Controls.Add(unitprices[i]);
}
Than You can in the button click set the value for each selected row:
// This code goes to the button click
// first empty the Textboxes:
foreach (TextBox productid in productids)
{
productid.Text = string.Empty;
}
foreach (TextBox productname in productnames)
{
productname.Text = string.Empty;
}
foreach (TextBox unitprice in unitprices)
{
unitprice.Text = string.Empty;
}
for (int i = 0; i < PTable.SelectedRows.Count; i++)
{
productids[i].Text = PTable.SelectedRows[i].Cells[0].Value.ToString();
productnames[i].Text = PTable.SelectedRows[i].Cells[1].Value.ToString();
// or better... Name the columns
// unitprices[i].Text = PTable.SelectedRows[i].Cells["unitPrices"].Value.ToString();
unitprices[i].Text = PTable.SelectedRows[i].Cells[4].Value.ToString();
}
I have a datagridview (two columns). Columns 1: Name, Columns 2: Age
And I want add more column name Evaluate behind Columns Age when I press Evaluate button.
Content on each row are Young(Old). Young if Age<40 and Old if Age>40
How I do that?
My code:
OpenFileDialog opd = new OpenFileDialog();
private void btnOpenfile_Click(object sender, EventArgs e)
{
//Open txt file
opd.Filter = "TXT|*.txt";
//Choose File
if (opd.ShowDialog() == DialogResult.OK)
{
lblFilename.Text = opd.SafeFileName;
}
var dulieu = File.ReadAllLines(opd.FileName);
////////////////////
if (dulieu.Count() > 0)
{
foreach (var cellValues in dulieu.Skip(0))
{
var cellArray = cellValues
.Split(new[] {'\t'}, StringSplitOptions.RemoveEmptyEntries);
if (cellArray.Length == dtgBangketqua.Columns.Count)
dtgBangketqua.Rows.Add(cellArray);
}
}
}
My Form
Based on your if (cellArray.Length == dtgBangketqua.Columns.Count) I assume you do not already have a "Evaluate" column within your DataGridView. So first thing to do is add one.
Then loop through each row and obtain the age value.
It originally came in as a string, so, you need to cast it to an int.
Then evaluate the age, I assume you did not mean to exclude people who are exactly 40, so I set that as Old.
Here is the code that should work for your need:
private void btnEvaluate_Click(object sender, EventArgs e)
{
dtgBangketqua.Columns.Add("Evaluate", "Evaluate");
foreach (DataGridViewRow ThisRow in dtgBangketqua.Rows)
{
string strAge = ThisRow.Cells[1].Value as string;
int Age = -1;
if( int.TryParse(strAge,out Age) == true )
{
ThisRow.Cells[2].Value = (Age < 40) ? "Young" : "Old";
}
}
}
Don't forget to mark the answer as accepted if it works for you.
Using C# and forms. I have a datagridview which has two of the three fields visible in the table which is editable and addable. The third field is in a combobox.
When updating the database (hitting enter after adding in the empty row) I get an error saying that the third field can't be null. I'd like to have the value in the combobox be used for the third field.
I can't seem to figure it out. Should I be using an event, somehow?
private void cmbMCID_SelectedIndexChanged(object sender, EventArgs e)
{
var cMarks = (from cm in DB.Student_Courses
where cm.CID == Convert.ToInt32(cmbMCID.Text)
select cm).ToList();
Student_Course sc = new Student_Course();
//cMarks.Add(sc);
studentCourseBindingSource.DataSource = cMarks;
sc.CID = Convert.ToInt32(cmbMCID.Text);
//studentCourseBindingSource.ResetBindings(false);
}
I've attempted to added a RowsAdded Event but didn't come out on top.
private void dgvSMarks_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
if (tabControl1.SelectedIndex == 3)
{
Student_Course sc = new Student_Course();
sc.CID = Convert.ToInt32(cmbMCID.Text);
}
}
Great, OK. so I was doing horrible things - with the combobox. A mix of needing to making a selection in the combobox first (otherwise returned null) and trying to commit what was already in the database.
Needed to create a for loop through the dgv collection, too.
private void dgvSMarks_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
// if (dgvSMarks.ContainsFocus == true)
// {
// // Get the RowIndex of the CID Cell, and add the combobox MCID value to it
// dgvSMarks.Rows[e.RowIndex-1].Cells[0].Value = Convert.ToInt32(cmbMCID.Text);
// }
var student_course = new Student_Course();
var course = new Course();
//MessageBox.Show("Row Count is " + dgvSMarks.Rows.Count);
for (int i = 1; i < dgvSMarks.Rows.Count; i++)
{
if (i == (dgvSMarks.Rows.Count - 1))
{
student_course.CID = Convert.ToInt32(cmbMCID.Text);
student_course.SID = Convert.ToInt32(dgvSMarks.Rows[i - 1].Cells[1].Value);
student_course.Mark = Convert.ToInt32(dgvSMarks.Rows[i - 1].Cells[2].Value);
DB.Student_Courses.InsertOnSubmit(student_course);
DB.SubmitChanges();
}
}
}
Thanks to the help of my CPI instructors. :)
I have been playing with this issue for a couple of hours and can't seem to get it to work.
It seems to not be searching the rows?
private void searchbutton_Click(object sender, EventArgs e)
{
string searchValue = searchtextBox.Text;
int rowIndex = 0;
inkGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
bool valueResulet = true;
foreach (DataGridViewRow row in inkGridView.Rows)
{
if (row.Cells[rowIndex].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
inkGridView.Rows[rowIndex].Selected = true;
rowIndex++;
valueResulet = false;
}
}
if (valueResulet != false)
{
MessageBox.Show("Unable to find "+ searchtextBox.Text,"Not Found");
return;
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
It just always throws the error.
I made a search textbox for a dataGridView I used so perhaps this is of use to you, since the controls are quite similar. although I chose to not give a message when it can't find it. Rather then a message it turns the textbox red, also it tries to find the first occurence of the complete text. If it can't find a complete match it will try to find a match that contains the search value
private void searchart()
{
int itemrow = -1;
String searchValue = cueTextBox1.Text.ToUpper();
if (searchValue != null && searchValue != "")
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
//search for identical art
if (row.Cells[0].Value.ToString().Equals(searchValue))
{
itemrow = row.Index;
break;//stop searching if it's found
}
//search for first art that contains search value
else if (row.Cells[0].Value.ToString().Contains(searchValue) && itemrow == -1)
{
itemrow = row.Index;
}
}
//if nothing found set color red
if (itemrow == -1)
{
cueTextBox1.BackColor = Color.Red;
}
//if found set color white, select the row and go to the row
else
{
cueTextBox1.BackColor = Color.White;
dataGridView1.Rows[itemrow].Selected = true;
dataGridView1.FirstDisplayedScrollingRowIndex = itemrow;
}
}
}
I would say that unless you've set DataGridView.AllowUserToAddRows to false you are getting a null reference exception once you try to access Value.ToString() on the last row. So either set that to false or if you want to allow adding of new rows just add a check
if (row.IsNewRow) continue;
It seems to me that your if-statement is the problem. Namely row.Cells[rowIndex] returns the Cells in each row and therefore what you named rowIndex and used in the if-statement is actually column index. So you can change it to, say:
if (row.Cells[0].Value.ToString().Equals(searchValue))
to search the first column. To check all Cell columns you have to loop over them in each row.
You might also wanna look over the use of rowIndex over all, since you first assigned it to 0 and incerement it in your loop. But, then you assign it to
rowIndex = row.Index;
and proceed to increment it (but only if you found a match). I think you just got your row and column indexes mixed up/intertwined.
Edit: And i presume the thrown exception is due to there not beeing enough columns as you loop over Cells (0, 0), (1, 1), (2, 2) ... and so forth instead of looping over rows with and a specified column.