Getting specific properties from a list - c#

i am facing a problem
i have a class
public class StudentDetails
{
int S_Detail_ID;
string address;
string email;
}
containg get set method and iam calling a function which is collecting the List<StudentDetails> and showing it in datagridview the problem is that list is returning all properties of the class, but i only want address and email not s_detail_id
here is code for the function
private void btnAddNewRowInGrid_Click(object sender, EventArgs e)
{
List<StudentDetails> lstStudentDetails = GetStudentDetails();
lststudentDetails.Add(new StudentDetails()); //what to do here, studentDetails is returning all properties but i want only addres and email
dataGridView1.DataSource = lstStudentDetails;
}
and GetStudent jusst count rows in datagridview and add a new row
private List<StudentDetails> GetStudentDetails()
{
lstStudentDetails = new List<StudentDetails>();
foreach (DataGridViewRow row in dataGridView1.Rows)
{
lstStudentDetails.Add(row.DataBoundItem as StudentDetails);
}
return lstStudentDetails;
}

Select DataGridView in designer and click on little triangle at top right corner of control
Click Edit Columns menu item
Manually add columns for address and email properties (type in property name in DataPropertyName item of column properties)
Disable columns auto-generation
You cannot turn off columns auto-generation from designer - its possible only from code:
dataGridView1.AutoGenerateColumns = false;
After that your code will work.

You can Try hiding the column you do not need by setting its visibility to false.
dataGridView1.Columns["S_Detail_ID"].Visible = false;

Related

How to get a ComboBox in a DataGridViewCell to drop down after a single click?

I have a DataGridView which has it's first column's style set to a ComboBox rather than the default TextBox. Since the number of rows in the DataGridView is not fixed on startup I cannot load in data to the ComboBoxes for each row when a new row is added. So I tried loading on the event of a user adding a row to the DataGridView:
public void myDataGridView_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
// Identifiers used are:
var myTableAdapter = new databaseTableAdapters.myTableTableAdapter();
var myDataTable = myTableAdapter.GetData();
int rowIndex = myDataGridView.CurrentcellAddress.Y;
var comboBoxCell = (DataGridViewComboBoxCell)myDataGridView.Rows[rowIndex].Cells[0];
string itemToAdd;
// Load in the data from the data table
foreach (System.Data.DataRow row in myDataTable.Rows)
{
// Get the current item to be added
itemToAdd = row[0].ToString();
// Make sure there are no duplicates
if (!comboBoxCell.Items.Contains(itemToAdd))
{
comboBoxCell.Items.Add(itemToAdd)
}
}
}
but this only will allow the user to see the drop down options after a second click. I would like to be able to have the user only click on the combo box once and see the options rather than the less intuitive double click. How can this be done?
The cell must gain focus for the drop down to occur, so the double click is actually a single click to gain focus on that cell and the second click is what causes the drop down to occur. So to see how to change focus following this link. I was able to modify the code with a single line of code
public void myDataGridView_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
// Identifiers used are:
var myTableAdapter = new databaseTableAdapters.myTableTableAdapter();
var myDataTable = myTableAdapter.GetData();
int rowIndex = myDataGridView.CurrentcellAddress.Y;
var comboBoxCell = (DataGridViewComboBoxCell)myDataGridView.Rows[rowIndex].Cells[0];
string itemToAdd;
// Load in the data from the data table
foreach (System.Data.DataRow row in myDataTable.Rows)
{
// Get the current item to be added
itemToAdd = row[0].ToString();
// Make sure there are no duplicates
if (!comboBoxCell.Items.Contains(itemToAdd))
{
comboBoxCell.Items.Add(itemToAdd)
}
}
// Send the focus to the next combo box (removes need for a double click)
myDataGridView.CurrentCell = myDataGridView.Rows[rowIndex + 1].Cells[0]; // <--- HERE
}

How make a DataGridVewLinkColumn sort with the rest of the DataGridView

I populated a DataGridView with a DataTable as DataSource. This DataSource has a column with comments in it. I hide this column as part of the requirements and added a new DataGridVewLinkColumn that when is clicked the user will be able to see that comment.
My problem is when I sort by clicking on any of the headers from the DataGridView, all the DataGridViewLinkColumn links disappear. I have set the SortMode to Automatic in this LinkColumn but seems that I need to do something else because still as soon as I click on the headers from the other columns in the Grid the links disappear.
Any one knows how can I make sure that when the DataGridView is sorted the link column gets sorted accordingly?
Many thanks
OK I figure it out. The problem is because I used a DataTable as DataSource it was binded to the grid and there is no way to add an extra column to a grid source that is already binded and expect that it will bind with the source.
To solve this problem I just modified the data table. Add the extra column in the data table with the strings that will be the links in the DataGridView and populate the DataGridView programatically as recommended in http://msdn.microsoft.com/en-us/library/bxt3k60s(v=vs.90).aspx
Column Sort Modes in the Windows Forms DataGridView Control
When a DataGridView control containing both bound and unbound columns is sorted, the values in the unbound columns cannot be maintained automatically. To maintain these values, you must implement virtual mode by setting the VirtualMode property to true and handling the CellValueNeeded and CellValuePushed events.
This is a bit complicated, so the simplest solution would be to add an extra column into your DataTable.
I'll leave an example below for future reference.
The points are:
VirtualMode should be true.
CellValueNeeded should be handled properly to show the specified cell values.
ColumnHeaderMouseClick should be handled properly to sort by the unbound columns, and to show sort glyphs.
Note:
The DataGridView in this example is read-only to make things simple.
This example form contains:
A typed DataSet, that has DataTable1 with columns of ID(string), Comment(string):
private DataSet1 dataSet1;
A BindingSource:
private BindingSource dataTable1BindingSource;
.DataMember = "DataTable1";
.DataSource = this.dataSet1;
A DataGridView:
private DataGridView dataTable1DataGridView;
.DataSource = this.dataTable1BindingSource;
.VirtualMode = true;
.CellValueNeeded += this.dataTable1DataGridView_CellValueNeeded;
.ColumnHeaderMouseClick += this.dataTable1DataGridView_ColumnHeaderMouseClick;
.ReadOnly = true;
.AllowUserToAddRows = false;
.AllowUserToDeleteRows = false;
Its columns:
private DataGridViewTextBoxColumn iDDataGridViewTextBoxColumn; // bound column
private DataGridViewTextBoxColumn commentDataGridViewTextBoxColumn; // bound column
private DataGridViewLinkColumn linkColumn; // unbound column
.SortMode = DataGridViewColumnSortMode.Automatic;
And here goes the code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Hold the link texts, in a dictinary
// keyed by ID (= unique key in DataTable1), to be bound to each row.
private SortedDictionary<string, string> _linkTexts
= new SortedDictionary<string, string>();
private void Form1_Load(object sender, EventArgs e)
{
// Bound data sample
this.dataSet1.DataTable1.AddDataTable1Row("1", "Comment1");
this.dataSet1.DataTable1.AddDataTable1Row("2", "Comment2");
this.dataSet1.DataTable1.AddDataTable1Row("3", "Comment3");
// Unbound data sample
this._linkTexts.Add("1", "linkA");
this._linkTexts.Add("2", "linkC");
this._linkTexts.Add("3", "linkB");
}
// Handles ColumnHeaderMouseClick to do custom sort.
private void dataTable1DataGridView_ColumnHeaderMouseClick(
object sender, DataGridViewCellMouseEventArgs e)
{
// When the unbound column header is clicked,
if (e.ColumnIndex == this.linkColumn.Index)
{
// Create a new DataView sorted by the link text
// with toggling the sort order.
DataView newView;
switch (this.linkColumn.HeaderCell.SortGlyphDirection)
{
case SortOrder.None:
case SortOrder.Descending:
this.linkColumn.HeaderCell.SortGlyphDirection
= SortOrder.Ascending;
newView = this.dataSet1.DataTable1
.OrderBy(row => this._linkTexts[row.ID])
.AsDataView();
break;
default:
this.linkColumn.HeaderCell.SortGlyphDirection
= SortOrder.Descending;
newView = this.dataSet1.DataTable1
.OrderByDescending(row => this._linkTexts[row.ID])
.AsDataView();
break;
}
// Set it as DataSource.
this.dataTable1BindingSource.DataSource = newView;
// Clear sort glyphs on the other column headers.
foreach (DataGridViewColumn col
in this.dataTable1DataGridView.Columns)
{
if (col != this.linkColumn)
col.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
// The bound column header is clicked,
else
{
// Sorting has done automatically.
// Reset the sort glyph on the unbound column.
this.linkColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
// Handles CellValueNeeded to show cell values in virtual mode.
private void dataTable1DataGridView_CellValueNeeded(
object sender, DataGridViewCellValueEventArgs e)
{
// Extract the bound row from the current data view.
DataSet1.DataTable1Row row
= (this.dataTable1BindingSource[e.RowIndex] as DataRowView)
.Row as DataSet1.DataTable1Row;
// For the unbound column,
if (e.ColumnIndex == this.linkColumn.Index)
{
if (row.IsIDNull())
e.Value = DBNull.Value;
else
// get the value from the dictionary.
e.Value = this._linkTexts[row.ID];
}
// For the bound columns,
else
{
// get the value from the data source.
string propName = this.dataTable1DataGridView
.Columns[e.ColumnIndex].DataPropertyName;
e.Value = row[propName];
}
}
}

Error after setting different datasources for datagridview combobox cells

I am trying to display in DataGridView control names and fields of database tables. In every row name of a table is displayed in the first cell, and second column allows to choose any of the fields of that table.
To do that, I am handling 'DataGridView.CellBeginEdit' event and fill DataSource for cell with names of the fields. When I am trying to edit those cells, provided list is displayed correctly and can b chose just fine.
However when I try to do the same in another row, I start getting DataError events about the cell I have edited.
Event arguments for DataRow have 'Formatting|Display' in Context field, and have the message "Value not allowed in DataGridViewComboBoxCell" (or close to it). In debug, the cell event references has correct Value field, but its DataSource is null and its FormattedValue is empty string. And previously displayed text changes to blank.
How should this be resolved correctly? Should I derive my own custom datagridview cell that displays text but has a combobox editor? If so, how?
Edit: here is code I am currently using:
public class FieldDataNeededEventArgs: EventArgs
{
public List<string> FieldNames
{
get; private set;
}
public string TableName
{
get; private set;
}
public ReferenceFieldDataNeededEventArgs(stringdata)
: base()
{
FieldNames = new List<string>();
TableName= data;
}
}
...
public event EventHandler<FieldDataNeededEventArgs> FieldDataNeeded =
new EventHandler<FieldDataNeededEventArgs>((sender, args) => { });
...
//handler for CellBeginEdit
//dgvMatch - dataGridView, DocsReferenceToTableMatch is class that datagridview is bound to
private void dgvMatch_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
if (e.ColumnIndex == dgvMatch.Columns["TableKey"].Index)
{
DocsReferenceToTableMatch data = dgvMatch.Rows[e.RowIndex].DataBoundItem as DocsReferenceToTableMatch;
FieldDataNeededEventArgs ea = new FieldDataNeededEventArgs(data.TableName);
FieldDataNeeded(this, ea);
var cell = (dgvMatch.Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewComboBoxCell);
cell.DataSource = ea.FieldNames;
}
}
//example handler for the FieldDataNeeded event
static void ld_ReferenceFieldDataNeeded(object sender, ReferenceFieldDataNeededEventArgs e)
{
for (int i = 0; i < 4; i++)
{
e.FieldNames.Add(String.Format("{0}_fld{1}", e.ReferenceName, i));
}
}

Access invisible columns in a Datagridview (WinForms)

I'm showing some data in a DataGridView using a list which I get from the Entity Framework. In this grid I set some database columns like the id to invisible.
When the user clicks on the gridview I need to know which object was clicked for further steps, the problem I cannot get the id column, neither through:
datagridview1.CurrentRow.Cells[0].Value // here I get only visible cells
nor through:
datagridview1.CurrentRow.DataBoundItem
It seems that through setting some columns to invisible the objects attached have anonymous types
Any ideas?
Thank you
I just tried this:
private void dataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var value = dataGridView.Rows[e.RowIndex].Cells[0].Value;
}
and it worked.
In my example column 0 is the hidden column, that contains the id property that you want to extract.
For me it works to take the columnName not the index.
so if your column has the name "id" you could use:
datagridview1.CurrentRow.Cells["id"].Value
I saw Jakub Kaleta's answer that was accepted as a best solution .after i test it, seems have a problem and it's YOU SHOULD CLICK ON TEXT DATAGIDVIEW CELL VALUE and it's not a pretty good!
I do it with below solution,and you can click every place of cell!
private void dataGridView_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView.SelectedCells.Count > 0)
{
int selectedrowindex = dataGridView.SelectedCells[0].RowIndex;
DataGridViewRow selectedRow = dataGridView.Rows[selectedrowindex];
_RoleID = int.Parse(Convert.ToString(selectedRow.Cells[1].Value));
_RoleName = Convert.ToString(selectedRow.Cells[2].Value);
}
}
it worked for me very well ;
You could us datakeys.
List datakeys for the hidden fields.
Then you access the values with the following code.
GridName.DataKeys[row.RowIndex][Index of key in datakey list].ToString()
For instance. Let's say your grid is named myGrid.
You have set the following datakeys:
Address
City
State
ZIPCode
Your code to access the Address would be:
myGrid.DataKeys[row.RowIndex[0].ToString();
You can add an extention:
public static Int32 ToInt32(this object value)
{
try
{
return Convert.ToInt32(value);
}
catch (Exception)
{
return 0;
}
}
public static object Get(this DataGridViewRow row,string columnName)
{
foreach (DataGridViewCell item in row.Cells)
{
if (item.OwningColumn.DataPropertyName.Equals(columnName))
{
return item.Value;
}
}
return null;
}
and call:
int id = gc.CurrentRow.Get("Id").ToInt32();

How to hide column of DataGridView when using custom DataSource?

I have a small app in c#, it has a DataGridView that gets filled using:
grid.DataSource = MyDatasource array;
MyClass hold the structure for the columns, it looks something like this:
class MyDatasource
{
private string column1;
private string column2;
public MyDatasource(string arg1, string arg2)
{
this.column1 = arg1;
this.column2 = arg2;
}
public string column1
{
get
{
return this.column1;
}
set
{
this.column1 = value;
}
}
public string column2
{
get
{
return this.column2;
}
set
{
this.column1 = value;
}
}
}
Everything works fine and the DataGridView gets populated with the correct data, but now I want to hide the column2. I tried adding [Browsable(false)] above the column declaration, that will hide it, but I also need to access the column value from code, and when I use [Browsable(false)] and try to read the content it acts like if the column doesn't exist. If I don't use it I can read the column without problem but it's visible in the DataGridView.
How could I hide the column but still be able to read its content from code?
In some cases, it might be a bad idea to first add the column to the DataGridView and then hide it.
I for example have a class that has an NHibernate proxy for an Image property for company logos. If I accessed that property (e.g. by calling its ToString method to show that in a DataGridView), it would download the image from the SQL server. If I had a list of Company objects and used that as the dataSource of the DataGridView like that, then (I suspect) it would download ALL the logos BEFORE I could hide the column.
To prevent this, I used the custom attribute
[System.ComponentModel.Browsable(false)]
on the image property, so that the DataGridView ignores the property (doesn't create the column and doesn't call the ToString methods).
public class Company
{
...
[System.ComponentModel.Browsable(false)]
virtual public MyImageClass Logo { get; set;}
You have to hide the column at the grid view control rather than at the data source. Hiding it at the data source it will not render to the grid view at all, therefore you won't be able to access the value in the grid view. Doing it the way you're suggesting, you would have to access the column value through the data source as opposed to the grid view.
To hide the column on the grid view control, you can use code like this:
dataGridView1.Columns[0].Visible = false;
To access the column from the data source, you could try something like this:
object colValue = ((DataTable)dataGridView.DataSource).Rows[dataSetIndex]["ColumnName"];
I have noticed that if utilised progrmmatically it renders incomplete (entire form simply doesn't "paint" anything) if used before panel1.Controls.Add(dataGridView); then dataGridView.Columns["ID"].Visible = false; will break the entire form and make it blank, so to get round that set this AFTER EG:
panel1.Controls.Add(dataGridView);
dataGridView.Columns["ID"].Visible = false;
//works
dataGridView.Columns["ID"].Visible = false;
panel1.Controls.Add(dataGridView);
//fails miserably
I"m not sure if its too late, but the problem is that, you cannot set the columns in design mode if you are binding at runtime. So if you are binding at runtime, go ahead and remove the columns from the design mode and do it pragmatically
ex..
if (dt.Rows.Count > 0)
{
dataGridViewProjects.DataSource = dt;
dataGridViewProjects.Columns["Title"].Width = 300;
dataGridViewProjects.Columns["ID"].Visible = false;
}
Set that particular column's Visible property = false
dataGridView[ColumnName or Index].Visible = false;
Edit
sorry missed the Columns Property
dataGridView.Columns[ColumnName or Index].Visible = false;
I had the same problem
Here is the Solution that might work for you. It worked for me
GridView1.DataBind();
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[0].Visible = false;
}
}
Just set DataGridView.AutoGenerateColumns = false;
You need click on the arrow on top right corner (in datagridview) to add columns, and in DataPropertyName you need to put a name of your property in your class.
Then, after you defined your columns in datagridview, you can set datagridview.datasource = myClassViewModel.
MyDataGridView.RowHeadersVisible = False;
Before binding and rename each columns header and set columns width.
To help my failing memory when I search, because I will search ... that's for sure ;-)
If you want to use the BrowsableAttribute, then you can look for it at runtime on the model and hide the column accordingly:
private void Form_Load(object sender, EventArgs e)
{
//add this line after your DataGridView initialization
HideColumns<MyModel>(myDvg);
}
private void HideColumns<T>(DataGridView dvg)
{
var type = typeof(T);
foreach (var column in dvg.Columns.Cast<DataGridViewColumn>())
column.Visible = IsBrowsable(type.GetProperty(column.Name));
}
private bool IsBrowsable(PropertyInfo propertyInfo)
{
var attribute = propertyInfo.GetCustomAttributes(true).FirstOrDefault(att => att.GetType() == typeof(BrowsableAttribute));
return attribute == null || (attribute as BrowsableAttribute).Browsable;
}
Try follow:
DataGridView1.Columns["YourColumnName"].Visible = false;

Categories