wpf how to edit datagrid row - c#

I'm using datagrid as the temporary list that until I decide to insert in the database all the data remain in datagrid. I'm adding functions like adding new records, deleting or editing. but I can not change selectedrow
my code is
gridlist.Items.Add(new { num_ins = num_ins.Text, dat_ins = DateTime.Now.ToShortDateString()} --> and many other value
and for delete
var selectedItem = gridlist.SelectedItem;
if (selectedItem != null)
{
gridlist.Items.Remove(selectedItem);
}
i want to make something like this
gridlist.Columns[0].gridlist.Items[1]= "my value";
I wanted to know if it is possible to do this directly from the datagrid as a removal or creation of new records

There are two possibilities.
If you always just add an anonymous type to the Items collection, then it is not possible no update the individual column values. You can however, update the entire row
gridlist.Items[0] = new { num_ins = 1, dat_ins = DateTime.Now};
If you could create a class/structure with public properties for the items being added, then you can update the individual properties of the items.
public class temp
{
public int num_ins {get;set;}
public DateTime dat_ins {get;set;}
}
((gridlist.Items[0] as temp)).num_ins = 3;

Related

How to set property and field values programmatically in WPF DataGrid?

I'm trying to create an DataGrid that automatically generates itself according to the Property and Fields coming from the different classes. Each class is created as a row and each property or field as a column. These classes have thousands of same Fields and Properties, but their values ​​are different.
There is no way to bind fields to DataGrid. So thats why i created columns dynamically like in this post: How to add Fields to DataGrid in WPF?. After running the code snippet below, I put the created properties from dynamic List (named dataObjects) to columns and then added the columns i created to DataGrid.
//Get all properties and fields from classes and send them into dynamic list
private void Property_ve_Field_Dinamik_Objeye_At()
{
//Sinif_Tutucu is an ObservableCollection which holds all Classes.
foreach (var item in Sinif_Tutucu)
{
//Get all fields from class
var fields = item.GetType().GetFields();
//Gett all properties from class
var properties = item.GetType().GetProperties();
dataObject = new System.Dynamic.ExpandoObject();
foreach (var field in fields)
{
dataObjectValues = (IDictionary<string, object>)dataObject;
dataObjectValues[field.Name] = field.GetValue(item);
}
foreach (var property in properties)
{
dataObjectValues = (IDictionary<string, object>)dataObject;
dataObjectValues[property.Name] = property.GetValue(item);
}
dataObjects.Add(dataObject);
}
dataObjectValues = dataObjects[0];
}
Here is definitions of variables used in the code above;
//Holds all classes
private ObservableCollection<object> _Sinif_Tutucu_ = new ObservableCollection<object>();
public ObservableCollection<object> Sinif_Tutucu //<--I'm getting the properties and fields from the classes in it.
{
get { return _Sinif_Tutucu_; }
set
{
if (value != _Sinif_Tutucu_)
{
this._Sinif_Tutucu_ = value;
}
}
}
//Holds rows and columns dynamically
private List<dynamic> _dataObjects_ = new List<dynamic>();
public List<dynamic> dataObjects //<-- This list is updating when i edit an item.
{
get { return _dataObjects_; }
set
{
if (value != _dataObjects_)
{
this._dataObjects_ = value;
}
}
}
dynamic dataObject;
IDictionary<string, object> dataObjectValues;
And DataGrid's source is like this;
DataGrid_1.ItemsSource = dataObjects;
Everything is so beautiful up to here. My DataGrid gets all fields and properties from all classes i have defined and shows their values correctly. When I edit the cells, I want it set the properties and fields in the classes. (SelectionMode = Single and SelectionUnit = Cell) But it doesn't update because I used the code I put above. Thats why, the values ​​in the DataGrid are no longer directly binded to the class. They are binded to the dynamic List. When I edit the values in the DataGrid, dynamic List is updated only instead of the property or fields in the classes.
When a value is edited, I tried to set it in the class it belongs to. But i don't want to do this manually. Because there is thousands of property and field. So i can't set them one by one. Program should understand which value i am changing in the DataGrid and which property or field going to be updated. Then it should update property or field in that class.
Or the list I am currently using is already being updated. It can be in a system that sets all values ​​in the list to properties and fields in classes when I press the button. (If anyone can tell me how to to this, this would be more useful. But any other solutions are can be too)
I tried to do the solutions I mentioned in the last two paragraphs but failed. I am new in the WPF world and still have problems coding. If there is any mistake in my English, sorry for that. I am learning English too. Finally, I've included a diagram below to make it clearer what I'm trying to do. Here is the diagram.
No answers.
It took me 8 days and I found the solution by trying. Here is the solution;
dataObjects' type is List<dynamic> and dataObjectValues' type is IDictionary<string, object>. It consists of dataObjects dataObjectValues. DataGrid's ItemSource is dataObjects. When i edit DataGrid, values are written to the list. Then I update the classes one by one using the list.
public void Siniflara_Veri_Yaz()
{
//Loop for rows
for (int i = 0; i<dataObjects.Count; i++)
{
//Get row
dataObjectValues = dataObjects[i];
//Loop for Column
for (int j = 0; j < dataObjectValues.Count; j++)
{
string Binding_Path = DataGrid_1.Columns[j].SortMemberPath;
//Get PropertyInfo if exist
PropertyInfo propInfo = Sinif_Tutucu[i].GetType().GetProperty(Binding_Path);
//If doesn't exist, it have to be a Field
if(propInfo != null)
{
//DataGrid uses textColumn (string)
//Get column types from Classes and cast string to whatever type the column is.
dataObjectValues[Binding_Path] = Tip_Degistir(propInfo.PropertyType.Name, dataObjectValues[Binding_Path].ToString());
propInfo.SetValue(Sinif_Tutucu[i], dataObjectValues[Binding_Path]);
}
//Same thing but for fields
else
{
FieldInfo fieldInfo = Sinif_Tutucu[i].GetType().GetField(Binding_Path);
dataObjectValues[Binding_Path] = Tip_Degistir(fieldInfo.FieldType.Name, dataObjectValues[Binding_Path].ToString());
fieldInfo.SetValue(Sinif_Tutucu[i], dataObjectValues[Binding_Path]);
}
}
}
}
Edit 1:
Sinif_Tutucu is an ObservableCollection which holds all classes I added to the DataGrid.

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.

How to get fields of DataGrid cell

In my WPF app, i have a Window(all code) that contains DataGrid. There's my DataGrid binding:
using (var db = new CompanyEntities())
{
var stocks = db.Stock;
var query = from s in stocks
select new { s.Id_Product, s.Quantity };
dataGrid.ItemsSource = query.ToList();
}
I want to read cell that is selected in DataGrid. I tried to read it using DataGrid.SelectedItem, but to get to the item i have to cast it to some type. I can't use Stock class, because my DataGrid cells contains 2 out of 3 fields of Stock. So i made another class to represent DataGrid cell:
class TableItem
{
public int Id_Product;
public int? Quantity;
}
And tried to cast it like this:
TableItem x = (TableItem)dataGrid.SelectedItem;
But i get InvalidCastException from type:
'<>f__AnonymousType02[System.Int32,System.Nullable1[System.Int32]]'
to my TableItem type.
So how should i get selected cell of my dataGrid?
My walkaround:
In my CompanyEntites constructor i set:
this.Configuration.ProxyCreationEnabled = false;
And now i can cast Stock s = (Stock)dataGrid.SelectedItem;without an exception, before switching ProxyCreation off, my SelectedItem was DynamicProxy.Stock~~ type.
Change your query like so:
var query = from s in stocks
select new TableItem { Id_Product = s.Id_Product, Quantity = s.Quantity };
And then cast SelectedItem to TableItem, now that it actually IS one. In C#, just because it looks like another class, doesn't mean it is that other class.
Alternatively, don't write a new class just because you happen not to need one property of the existing class. Just use the existing class:
// Don't think you need to call ToList() here
dataGrid.ItemsSource = db.Stock;
...and cast SelectedItem to whatever that is.
my DataGrid cells contains 2 out of 3 fields of Stock:
So what? Is the problem that you get an extra column? Define the columns explicitly, just the ones you need.

Edited cell was reset after change focus in gridview using devexpress 15.2 c#

i'm newbie in devexpress. when i add new row and fill some value to one cell, then i focus to other cell in same row i lost value in that cell.
This is my code (column name in gridview match with coumn in Datatable dtGroupRole)
FrmUser.cs
...
adminGroupRoles = AdminGroupRoles.GetAllGroupRoles().ToList();
dtGroupRole = GlobalVars.ToDataTable<AdminGroupRoles>(adminGroupRoles);
grdGroupRoles.DataSource = adminGroupRoles;
AdminGroupRoles.cs
public class AdminGroupRoles
{
public int GroupId { get; set; }
public string GroupName { get; set; }
public static List<AdminGroupRoles> GetAllGroupRoles()
{
return AdminGroupRoles.Inst.ExeStoreToList("sp_AdminUsers_GetAllGroupRoles");
}
}
The edited cell was reset:
Thanks all :)
I see that you assign adminGroupRoles as the grid source. It's a List. So that the grid can add new rows, the data source should be IBindingList. adminGroupRoles doesn't implement it. As far as I understand, dtGroupRole is DataTable converted from adminGroupRoles. So, you need to use as the grid data source:
adminGroupRoles = AdminGroupRoles.GetAllGroupRoles().ToList();
dtGroupRole = GlobalVars.ToDataTable<AdminGroupRoles>(adminGroupRoles);
grdGroupRoles.DataSource = dtGroupRole;
Use BindingList instead of List as Gosha_Fighten replied. Then you can convert your bindinglist to list like:
List <T> list = yourbindinglist.Select(b => b).ToList();
OR convert your list<T> to bindinglist<T> like:
yourBindingList = new BindingList<T>(listToConvert);

How do I bind a DataGridViewComboBoxColumn to a property/method of an object that returns a list?

I have a custom object with several properties, one of which returns a list. This is the code for the object:
public class SearchResult
{
private int eventId;
private String eventTitle;
private int startDate;
private List<String> tags;
// Properties
public int EventId { get { return this.eventId; } }
public String EventTitle { get { return this.eventTitle; } }
public int StartDate { get { return this.startDate; } }
public List<String> Tags { get { return this.tags; } }
public SearchResult(int eventId, String eventTitle, int startDate, List<String> tags)
{
// Constructor code
}
public List<String> GetTags()
{
return this.tags;
}
}
I also have a DataGridViewComboBoxColumn that I want to bind to the Tags property. Basically, each SearchResult object will be displayed in its own row, and I want the List<String> in the Tags property of each object to be displayed in a ComboBox cell in that row. This is the code I have so far for my DataGridView:
BindingList<SearchResult> results = new BindingList<SearchResult>();
results.Add(new SearchResult(1, "This is a title", 2012, new List<String> { "Tag1", "Tag with a long name1" }));
results.Add(new SearchResult(2, "The quick brown fox", 2012, new List<String> { "Stack", "Overflow" }));
results.Add(new SearchResult(3, "In this tutorial, you create a class that is the type for each object in the object collection. ", 2012, new List<String> { "NYSE", "FTSE" }));
results.Add(new SearchResult(4, "another long piece of title text", -999, new List<String> { "Rabbits", "Chickens" }));
MyDataGrid.AutoGenerateColumns = false;
MyDataGrid.AllowUserToAddRows = false;
MyDataGrid.AllowUserToDeleteRows = false;
MyDataGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.None;
MyDataGrid.BackgroundColor = System.Drawing.SystemColors.Control;
MyDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
MyDataGrid.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders;
MyDataGrid.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.DisplayedCells;
MyDataGrid.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
DataGridViewTextBoxColumn eventIdColumn = new DataGridViewTextBoxColumn();
eventIdColumn.DataPropertyName = "EventId";
eventIdColumn.HeaderText = "Event ID";
eventIdColumn.ReadOnly = true;
eventIdColumn.Width = 84;
DataGridViewTextBoxColumn eventTitleColumn = new DataGridViewTextBoxColumn();
eventTitleColumn.DataPropertyName = "EventTitle";
eventTitleColumn.HeaderText = "Event Title";
eventTitleColumn.ReadOnly = true;
eventTitleColumn.Width = 368;
DataGridViewTextBoxColumn startDateColumn = new DataGridViewTextBoxColumn();
startDateColumn.DataPropertyName = "StartDate";
startDateColumn.HeaderText = "Start Date";
startDateColumn.ReadOnly = true;
startDateColumn.Width = 130;
//I think I need to insert the code for the tags column here, but I'm not sure
MyDataGrid.Columns.Add(eventIdColumn);
MyDataGrid.Columns.Add(eventTitleColumn);
MyDataGrid.Columns.Add(startDateColumn);
//MyDataGrid.Columns.Add(tagsColumn);
MyDataGrid.DataSource = results;
I derived this code from a tutorial I found online, and it works perfectly.
I've been trying to bind the Tags property of SearchResult to a DataGridViewComboBoxColumn, but I'm not sure how. I've been looking at this question, which provides this code:
column.DataPropertyName = "Foo";
column.DisplayMember = "SomeNameField";
column.ValueMember = "Bar"; // must do this, empty string causes it to be
// of type string, basically the display value
// probably a bug in .NET
column.DataSource = from foo in Foo select foo;
grid.DataSource = data;
The reason I'm having trouble is because of a few nuances of the linked question that I don't understand.
According to the documentation and the linked question, DisplayMember should be linked to the property that "contains a description of the instance", but since SearchResult objects are added dynamically and don't have any description associated with them, should I just leave it blank?
ValueMember is giving me similar problems, since I'm unsure what to put even after reading its documentation.
In the linked question, the accepted answer binds the entire datagrid at once using LINQ. Is that how I should be doing this? I'm not sure how to modify that code for my situation, but I thought it would be something along these lines.
:
tagsColumn.DataPropertyName = "Tags";
tagsColumn.DisplayMember = ""; // I'm unsure of what to put here
tagsColumn.ValueMember = ""; // Once again, I don't know what to set this to
I also presume I should have a line that sets the DataSource for the column, e.g.
tagsColumn.DataSource = <some LINQ query, perhaps?>
but I don't know because the only mostly relevant C# source I've been able to find is that question.
UPDATE:
I did find a second question that suggests code similar to this for data binding:
// reference the combobox column
DataGridViewComboBoxColumn cboBoxColumn = (DataGridViewComboBoxColumn)dataGridView1.Columns[0];
cboBoxColumn.DataSource = Choice.GetChoices();
cboBoxColumn.DisplayMember = "Name"; // the Name property in Choice class
cboBoxColumn.ValueMember = "Value"; // ditto for the Value property
Based on that, I a) added the GetTags() method to SearchResult and added this code into my DataGridView initialisation code:
DataGridViewComboBoxColumn tagsColumn = new DataGridViewComboBoxColumn();
tagsColumn.DataSource = SearchResult.GetTags(); // ERROR
tagsColumn.DisplayMember = ""; // Still not sure
tagsColumn.ValueMember = ""; // ??
However, Visual Studio gives me an error on the second line when I try to run this:
An object reference is required for the non-static field, method, or property 'SearchResult.GetTags()'
UPDATE 2:
I'm still searching around for this without success. I don't understand how with other properties (e.g. EventId) I can simply declare the data property name as EventId, and it will display in the table, but I cannot do this with ComboBox columns.
Since the objects are instantiated in a separate class and put in a list, it doesn't seem to make sense to me that I should have to loop through the entire array of objects (of which there may be several hundred) to bind the Tags property to the ComboBox column for each instance, when I don't need to loop through the list of SearchResult objects to bind other properties, e.g. EventId.
Why does this binding-properties-by-name only work for some properties and not others?
I don't quite understand why you want to use DataGridViewComboBoxColumn to display a list of elements. This column kind is designed to allow user to select one of many possibilities. It seams it is not your case because you don't have public string SelectedTag{get;set;} property to store it. As I understand your model you have many tags already selected for your SearchResult and you want to display them in grid.
As documentation states:
http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewcomboboxcolumn.datasource
Getting or setting this [DataSource] property gets or sets the DataSource property of the object returned by the CellTemplate property. Setting this property also sets the DataSource property of every cell in the column and refreshes the column display. To override the specified value for individual cells, set the cell values after you set the column value.
DataGridViewComboBoxColumn simply does not have capability to bind items property to data source because it assumes that there is only one list of elements that is used as data source for all rows of data grid.
I also assume that you would set ReadOnly = true property for this column as you have for all other. If so it would prevent user form seeing list of tags because drop down list would never be displayed.
If you wand to display list of strings in read only mode I would suggest to flatten this list of tags to single string:
public string Tags { get { return string.Join(", ", tags); } }
and display it in text column.
For the error , i can suggest you to make an instance of the class and then call the method as its not static or you can make your method static.
Moreover As you needs the comboboxcolumn ,
DataGridViewComboBoxColumn tagsColumn = new DataGridViewComboBoxColumn();
tagsColumn.DataSource = SearchResult.GetTags(); // ERROR
tagsColumn.DisplayMember = ""; // Still not sure
tagsColumn.ValueMember = ""; // ??
Mostly we have dropdowns for objects like Country(id,name) so DisplayMember = name will be shown as text in dropdown while ValueMember = id will be used in the referencing tables in database.But this is not your case.
Here you have a list of strings to show in dropdown , so you don't need to set them.
As written here
If the DataSource property is set to a string array, then ValueMember
and DisplayMember do not need to be set because each string in the
array will be used for both value and display.

Categories

Resources