Selected row (object) in dataGridView C# - c#

I have class
public class Students
{
public int StudentsID { get; set; }
public string name{ get; set; }
}
And I use a list of objects. Then I add the objects from the file and display this list in datagridview like that:
List<Students> listStudents = new List<Students>();
for (int i = 0; i < listStudents.Count; i++)
{
dataGridView2.Rows.Add(listStudents[i].name, listStudents[i].StudentsID);
}
And now is the question. How would I know which object is selected.
I tried this, but it does not work - currentObject is null
private void btn_Click(object sender, EventArgs e)
{
Students currentObject = (Students)dataGridView1.CurrentRow.DataBoundItem;
}

People who are new to using Winforms and DataGridViews, tend to add the rows of the DataGridView themselves.
But there is a much easier way: use the DataSource of the DataGridView.
You plan to show Students in your DataGridView. You'll probably have two columns, the one where you show the StudentsId, and the one where you show the Name.
Every DataGridViewColumn has a string property DataGridViewColumn.dataPropertyName This property contains the name of the property that this column should show.
So to show the Id and the name, you need something like:
columnStudentId.DataPropertyName = nameof(Student.StudentId);
columnStudentName.DataPropertyName = nameof(Student.Name);
You will probably set these properties using the visual studio designer.
Now to show the Students, one Student per row, all you have to do is assign the collection to the DataSource of the DataGridView:
List<Student> students = ...
this.DataGridView1.DataSource = students;
And bingo, the names and id of every Student is shown in the DataGridView.
The solution is a one-time only: changes are not recorded. If you want that changes in the table are updated automatically, put the items in a BindingList<Student>:
private BindlingList<Student> students;
private void DisplayStudents(IEnumerable<Student> students)
{
this.students = new BindingList<Student>(students.ToList());
this.dataGridView1.DataSource = this.students;
}
Any changes made by the operator are automatically updated in this.students, any change that you make to this.Students is automatically displayed.
private void AddOrUpdateStudent(Student studentToUpdate)
{
// Is there already a Student with this Id shown?
var existingStudent = this.Students
.Where(student => student.StudentId == studentToUpdate.StudentId)
.FirstOrDefault();
if (existingStudent != null)
{
// Student exists, update the name:
existingStudent.Name == studentToUpdate.Name;
}
else
{
// Student does not exist yet; add it:
this.Students.Add(studentToUpdate);
}
}
The datagrid view is automatically updated.
Similarly the other way round: operator edits one or more cells in the datagridview and presses a button:
private void OnButtonOk_Clicked(object sender, ...)
{
// all edited cells are already updated in this.Students:
this.ProcessStudents(this.Students);
}
To get the Student that is displayed in a certain row, you can use property DataGridViewRow.DataBoundItem
IEnumerable<Student> selectedStudents = this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => (Student)row.DataBoundItem);

remove this code:
for (int i = 0; i < listStudents.Count; i++)
{
dataGridView2.Rows.Add(listStudents[i].name, listStudents[i].StudentsID);
}
add your list to Datagridview datasource :
dataGridView2.DataSource = listStudents;
to get the current row as object :
Students currentObject = (Students)dataGridView2.CurrentRow.DataBoundItem;

Just do this:
List<Students> listStudents = new List<Students>();
...
dataGridView2.DataSource = listStudents;
And get rid of the "for" loop.

Related

Editing textbox inside datagridview

My project is c# windows form with Entity Framework , and I have DataGridView with a TextColumn. I want to edit the last column TextColumn Cells[3].
To get all values from database to GridView is not problem. I get them.
And I can put a new value but as soon as I click ontherplace then it changes to the old value. Textbox is not keeping the new value that I want to edit.
ReadOnly is false for this cells in column 3 becouse I can write but changes back the new value to the old values.
What kind of events I'am missing and how shall I do to fix this problem? By some how it seems like the hole Gridview is locked. Please Help.
I tried even:
foreach (DataGridViewRow row in dgvOrder.Rows)
{
row.Cells[3].ReadOnly = false;
}
Here below is my code.
private void Treatments_Load(object sender, EventArgs e)
{
try
{
using (MyHealthEntities db = new MyHealthEntities())
{
var orderd = db.Order.Where(x => x.Ordernr == OrNr).FirstOrDefault();
if(orderd != null)
{
var myOrder = (from u in db.....
join d in ...
join m in ...
where u.....
select new
{
OrderId = m.MedId,
Name = m. Name,
Quality = m.Quality,
Description = d.Description
}
).ToList();
if (myOrder != null)
{
dgvOrder.DataSource = myOrder ;
}
}
foreach (DataGridViewRow row in dgvOrder.Rows)
{
row.Cells[3].ReadOnly = false;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
If I'm not mistaken, a normal System.Collections.Generic.List will not support editing when directly bound to because it does not implement IBindingList (or the necessary ListChanged event) for two-way data binding. You will likely need to wrap the List into a BindingList in order to edit the values. This will not be possible with an anonymous type.
First, create a class to store your List items:
public Class Order {
public int OrderID { get; set; }
public string Name { get; set; }
public byte Quality { get; set; }
public string Description { get; set; }
}
Then in your query,
select new Order
{
OrderId = m.MedId,
Name = m. Name,
Quality = m.Quality,
Description = d.Description
}
Now move it to a BindingList:
if (myOrder != null) {
BindingList<Order> myBindingList = New BindingList<Order>(myOrder);
dgvOrder.DataSource = myBindingList;
}
I would also recommend wrapping the BindingList in a BindingSource, which will prevent you from having to handle rows being added/deleted manually:
if (myOrder != null) {
BindingList<Order> myBindingList = New BindingList<Order>(myOrder);
BindingSource myBindingSource = New BindingSource(myBindingList);
dgvOrder.DataSource = myBindingSource;
}
Apologies if my syntax is a little off, I've been mostly using vb as of late.
EDIT: I missed that BindingList does not implement IContainer, so the above code for binding to a BindingSource will not work because the single-parameter constructor for BindingSource specifically takes an IContainer. If you still want to use a BindingSource, the third constructor of BindingSource should be used instead, like so:
BindingSource myBindingSource = New BindingSource(myBindingList, Nothing);
BindingSource does accept binding to an IBindingList, but only using this constructor or by directly setting the .DataSource property after using the parameterless constructor.
Leaving the erroneous code above so others who read OP's comment will understand what was being referenced.
You can't edit DataGrid itens when you are using "dgvOrder.DataSource = myOrder".
My suggestion is you do and "for each" in your myOrder list and add row by Row into Grid. After, you can do an other "for each", recovery the datas from DataGrid and Save the itens in your db.Order.

DataGridView Row Tag

I have a Datagridview, and a field from the database. I want save this field to row tag in gridview, but after saving to the tag with a Foreach loop, in another function I see it returns null value.
How do I save this field?
Thank you
private void UC_DKHoc_Load(object sender, EventArgs e)
{
var dsLop = LopDangKyServices.LayDanhSachLopDangKy();
var DatasourceGV = from lop in dsLop
select new
{
lop.MaLopDangKy,
lop.TenLopDangKy,
lop.CLB1.TenCLB,
lop.NamHoc,
lop.HocPhi,
lop.LichHoc
};
advancedDataGridView.DataSource = DatasourceGV.ToList(); // Display to gridview
int i = 0;
foreach (DataGridViewRow row in advancedDataGridView.Rows)
{
row.Tag = dsLop[i++].CLB; //save complete
}
advancedDataGridView.DisableFilter(STT);
}
In The function I see null values.
private void simpleButton1_Click(object sender, EventArgs e)
{
MessageBox.Show(advancedDataGridView.Rows[1].Tag.ToString()); //Null value
}
You can use either of the following options:
Include all the properties which you need in the list (including CLB). Then after assigning the list as DataSource of the DataGridView, hide the corresponding column:
advancedDataGridView.DataSource = list;
advancedDataGridView.Columns["CLB"].Visible = false;
Create a model class containing all the properties which you need, including CLB. Then just decorate CLB with [Browsable(false)] attribute. (You need to select the result by creating instance of the model class, rather than anonymous type.)
[Browsable(false)]
public int CLB { get; set;}
Set AutoGenerateColumns of DataGridView to false and add desired columns to DataGridView. Make sure you set Name, DataPropertyName and HeaderText. Then set DataSource of DataGridView to the list which contains all properties, including CLB:
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn() {
Name = "MaLopDangKy",
DataPropertyName = "MaLopDangKy",
HeaderText = "MaLopDangKy" });
//Add rest of columns ...
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = list;
You can decide to add CLB as an invisible column of DataGridView. Even if you didn't add it as a column, you can access to the DataBoundItem property of the DataGridViewRow which points to the list member instance which is displaying the the row. Cast it to the list member type and use its CLB property.

Calling a void function in datagridview c#

How can I call my function in this data gridview? I would like to make the sum of these two to be displayed in the highlighted datagridview:
foreach (DataRow row in dt.Rows) {
datagridview_information.Rows.Add();
datagridview_information.Rows[counterSelected].Cells[0].Value = row["BrandName"].ToString();
datagridview_information.Rows[counterSelected].Cells[1].Value = row["GenericName"].ToString();
datagridview_information.Rows[counterSelected].Cells[2].Value = row["PresDayOfIntake"].ToString();
datagridview_information.Rows[counterSelected].Cells[3].Value = row["Status"].ToString();
datagridview_information.Rows[counterSelected].Cells[4].Value = "5";
datagridview_information.Rows[counterSelected].Cells[5].Value = medicineLeft();
datagridview_information.Rows[counterSelected].Cells[7].Value = row["ContainerNumber"].ToString();
datagridview_information.Rows[counterSelected].Cells[8].Value = row["Status"].ToString(); ;
counterSelected++;
}
}
}
public int medicineLeft()
{
for (int i = 0; i < datagridview_schedule.Rows.Count; ++i)
{
medleft += Convert.ToInt32(datagridview_information.Rows[i].Cells[8]) - Convert.ToInt32(datagridview_medicine.Rows[i].Cells[4]);
datagridview_information.Rows[i].Cells[5].Value = Convert.ToString(medleft);
}
return medicineLeft();
}
You're going to have to give more code here, I have absolutely no idea what you're trying to do. Is medleft declared elsewhere?
Your medicineLeft() method is returning itself, meaning it'll run as an infinite loop and will probably throw a StackOverflow exception on this line:
datagridview_information.Rows[counterSelected].Cells[5].Value = medicineLeft();
Your medicineLeft method needs to return an integer - is it meant to return medleft? If so, changing it to return medleft; should fix this.
One easy solution would be to change the way you populate the DataGridView. If I were doing this, I would do the following:
Create a class object to store the data you want in your grid:
public class Medicine
{
public string BrandName { get; set; }
public string GenericName { get; set; }
<... add other properties ...>
}
Instantiate a collection of Medicine objects that you want to bind to the DataGridView.
public List<Medicine> GetMedicine(DataRowCollection rows)
{
List<Medicine> medicines = new List<Medicine>();
foreach (DataRow row in rows)
{
Medicine medicine = new Medicine();
medicine.BrandName = row["BrandName"] == null ? string.Empty : row["BrandName"].ToString();
medicine.GenericName = row["GenericName"] == null ? string.Empty : row["GenericName"].ToString();
//more rows here to populate Medicine object
//include the call to whatever methods are needed to populate the object
medicines.Add(medicine);
}
return medicines;
}
Bind the collection to the DataGridView
datagridview_information.DataSource = GetMedicines(dt.Rows);
I find it easy to create the DataGridView control on the form, set the columns in the designer, and set the AutoGenerateColumns property to false in code so that the grid appears as I wish. It is up to you how you want to handle that.

Checked List Box Specific item Checked When Double click DataGridView On Windows Form Application

i am using CheckedListBox so that user can multi select items for this i am populating CheckedListBox dynamically from database Here is CheckedListBox Filling Method
public void FillSubjectsCombo()
{
DataTable dt = objSubCls.FillSubjects();
chkLstBxClass_FrmSubjecClassRelation.DataSource = dt;
chkLstBxClass_FrmSubjecClassRelation.DisplayMember = "Subjects";
chkLstBxClass_FrmSubjecClassRelation.ValueMember = "SubId";
chkLstBxClass_FrmSubjecClassRelation.Enabled = true;
for (int i = 0; i < dt.Rows.Count; i++)
{
//Here i am setting Every item Checked
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Checked);
}
}
On the Same Windows Form I have DataGridView i Want when i double click any row of datagrid then from selected row get value and from that value make respected item checked in CheckedListBox and other item UnChecked
Here is the DataGridView Event
private void dgv_FrmSubjectClassRelation_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
string classId = dgv_FrmSubjectClassRelation.CurrentRow.Cells[3].Value.ToString();
string className = dgv_FrmSubjectClassRelation.CurrentRow.Cells[4].Value.ToString();
foreach (int i in chkLstBxClass_FrmSubjecClassRelation.CheckedIndices)
{
//Here I am UnChecking Every Checked Item
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Unchecked);
}
My Question : How to Checked The Specific Item When Double Clicking DataGridView
Update: I am binding My DataGridView Like This
for (int i = 0; i < dt.Rows.Count; i++)
{
dgv_FrmSmstrClsAssign.Rows.Add();
dgv_FrmSmstrClsAssign.Rows[i].Cells[0].Value = dt.Rows[i].ItemArray[0];//Acadmc Yr
dgv_FrmSmstrClsAssign.Rows[i].Cells[1].Value = dt.Rows[i].ItemArray[1];// Semester Name
dgv_FrmSmstrClsAssign.Rows[i].Cells[2].Value = dt.Rows[i].ItemArray[2]; //College
dgv_FrmSmstrClsAssign.Rows[i].Cells[3].Value = dt.Rows[i].ItemArray[3];//Class
dgv_FrmSmstrClsAssign.Rows[i].Cells[4].Value = dt.Rows[i].ItemArray[4]; //Entry Date
dgv_FrmSmstrClsAssign.Rows[i].Cells[5].Value = dt.Rows[i].ItemArray[5];//IsActive
dgv_FrmSmstrClsAssign.Rows[i].Cells[6].Value = dt.Rows[i].ItemArray[6];//AcadmicYr Id
dgv_FrmSmstrClsAssign.Rows[i].Cells[7].Value = dt.Rows[i].ItemArray[7];//Semster Id
dgv_FrmSmstrClsAssign.Rows[i].Cells[8].Value = dt.Rows[i].ItemArray[8];//Semster Id
}
I was unable to find any method that allows you to easily map the bound value, so you will have to use IndexOf method of Items collection to obtain the index and then manually check-uncheck the items.
To obtain the bound item from DataGridView row you can use DataGridViewRow.DataBoundItem property:
private void CheckSelectedItem()
{
// Get bound item object from datagrid
object item = dgv_FrmSubjectClassRelation.CurrentRow.DataBoundItem;
// Get corresponding index in listView
Int32 itemIndexInCheckedListView = chkLstBxClass_FrmSubjecClassRelation.Items.IndexOf(item);
// Check the item in listView
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(itemIndexInCheckedListView,
CheckState.Checked);
}
private void dgv_FrmSubjectClassRelation_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
string classId = dgv_FrmSubjectClassRelation.CurrentRow.Cells[3].Value.ToString();
string className = dgv_FrmSubjectClassRelation.CurrentRow.Cells[4].Value.ToString();
foreach (int i in chkLstBxClass_FrmSubjecClassRelation.CheckedIndices)
{
//Here I am UnChecking Every Checked Item
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(i, CheckState.Unchecked);
}
// --------------Check the selected item----------------
this.CheckSelectedItem();
}
EDIT:
What you do is not exactly binding (well, it is binding, just not as Windows Forms defines it), so the previous solution won't work for you. If both your DataTable and DataGridView contain primary key or another unique identifier, then it is possible to map CurrentRow to the Item in DataTable:
private void CheckSelectedItem()
{
// Get bound item object from datagrid
object uniqueKey = dgv_FrmSubjectClassRelation.
CurrentRow.
Cells["SOME UNIQUE VALUE COLUMN"].
Value;
// Adapting http://stackoverflow.com/a/9300237/3745022 - there are more simple LINQless
// solutions for this situation, but it is not important for the concept.
Int32 itemIndexInCheckedListView = chkLstBxClass_FrmSubjecClassRelation.
Items.
Select((value, index) => new { value, index }).
Where(pair => pair.value.UniqueValue == uniqueKey ).
Select(pair => pair.index + 1).
FirstOrDefault() - 1;
// Check the item in listView
chkLstBxClass_FrmSubjecClassRelation.SetItemCheckState(itemIndexInCheckedListView,
CheckState.Checked);
}
If you do not have such Unique column you may want to add it(just make it hidden)
OR even better - use full-blown DataBinding - http://msdn.microsoft.com/en-us/library/fbk67b6z(v=vs.90).aspx;

Adding data to data bound DataGridView (WinForms, C#)

I have 3 tables:
Order (OrderId, columnXY ..)
OrderItem (OrderId, ItemId, Quantity)
Item (ItemId, Name, Price)
The order table is bound to a DataGridView. On selection changed for the order dgv, an orderedItem dgv is populated like this:
view.GetOrderedItemDataGridView().DataSource = dataContext.OrderItemSet.Where(o => o.OrderId == orderId).Select(o => new { o.Item.Id, o.Quantity, o.Item.Price }).ToList();
Upon pressing an AddItemButton, a dialog opens to pick an item, after which the item should be added to the orderedItem dgv:
using (var form = new OrderItemView(dataContext.ItemSet.ToList()))
{
if (form.ShowDialog() == DialogResult.OK)
{
// Add selected item from OrderItemView to orderedItem dgv
}
else
{
}
}
The changes are supposed to be saved later, by clicking the SaveOrderButton, or canceled by clicking the CancelOrderButton.
void view_SaveOrderClicked(object sender, EventArgs e)
{
// Get order data
int customerId = Convert.ToInt32(view.OrderCustomerID);
order.CustomerId = customerId;
order.Weight = Convert.ToDouble(view.OrderWeight);
order.Sum = Convert.ToDecimal(view.OrderSum);
order.Date = view.OrderDate;
order.AddressId = dataContext.AddressSet.Where(c => c.CustomerId == customerId && c.IsDeliveryAddress == true)
.Select(a => a.Id).SingleOrDefault();
if (!orderUpdateMode)
{
dataContext.OrderSet.Add(order);
}
else
{
dataContext.Entry(order).State = EntityState.Modified;
}
dataContext.SaveChanges();
}
I can't figure out how to add a new item to the dgv, since I can't add rows directly to a data bound dgv. Also, I'm populating the dgv with an anonymous type, so I can't have a class property to use as data source to which I add the new item. Should I maybe make a new object to fill the dgv, using only the properties I want displayed? I'm retrieving the data the way I do right now so that only certain columns are added to the orderedItem dgv.
How can I solve this problem?
Cheers!
EDIT:
When using BindingSource I run into problems because the type I get in the OnSelectionChanged event method of the Order dgv ...
orderedItemsBS.DataSource = dataContext.OrderItemSet.Where(o => o.OrderId == orderId).
Select(o => new { o.Item.Id, o.Item.Name, o.Quantity, o.Item.Price }).ToList();
is not the same as the one I get after selecting an orderItem:
void view_NewOrderItemClicked(object sender, EventArgs e)
{
using (var form = new OrderItemView(dataContext.ItemSet.ToList()))
{
if (form.ShowDialog() == DialogResult.OK)
{
var item = new { Id = form.Item.Id, Name = form.Item.Name, Quantity = form.Quantity, Price = form.Item.Price };
orderedItemsBS.Add(item);
view.GetOrderedItemDataGridView().DataSource = orderedItemsBS;
}
}
}
Also, I don't want to save anything to the db in the NewOrderItemClicked method since the user might press the CancelButton after editing/adding an order.
Either use BindingSource and add items to it (will automatically update DataGridView that is bound to (tutorial) or rebind data source after adding:
view.GetOrderedItemDataGridView().DataSource = null;
view.GetOrderedItemDataGridView().Rows.Clear();
view.GetOrderedItemDataGridView().DataSource = dataContext.OrderItemSet.Where(o => o.OrderId == orderId).Select(o => new { o.Item.Id, o.Quantity, o.Item.Price }).ToList();
After edit:
First of all: do not use anonymous type, create normal class and work with it.
Second:
view.GetOrderedItemDataGridView().DataSource = orderedItemsBS;
this line is not needed, instead add this: orderedItemsBS.ResetBindings(false);

Categories

Resources