I'm having a Structure like
X={ID="1", Name="XX",
ID="2", Name="YY" };
How to dump this data to a DataGridView of two columns
The gridView is like
ID | Name
Can we use LINQ to do this. I'm new to DataGridView Pleaese help me to do this..
Thanks in advance
first you need to add 2 columns to datagrid. you may do it at design time. see Columns property.
then add rows as much as you need.
this.dataGridView1.Rows.Add("1", "XX");
Let's assume you have a class like this:
public class Staff
{
public int ID { get; set; }
public string Name { get; set; }
}
And assume you have dragged and dropped a DataGridView to your form, and name it dataGridView1.
You need a BindingSource to hold your data to bind your DataGridView. This is how you can do it:
private void frmDGV_Load(object sender, EventArgs e)
{
//dummy data
List<Staff> lstStaff = new List<Staff>();
lstStaff.Add(new Staff()
{
ID = 1,
Name = "XX"
});
lstStaff.Add(new Staff()
{
ID = 2,
Name = "YY"
});
//use binding source to hold dummy data
BindingSource binding = new BindingSource();
binding.DataSource = lstStaff;
//bind datagridview to binding source
dataGridView1.DataSource = binding;
}
My favorite way to do this is with an extension function called 'Map':
public static void Map<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (T i in source)
func(i);
}
Then you can add all the rows like so:
X.Map(item => this.dataGridView1.Rows.Add(item.ID, item.Name));
LINQ is a "query" language (thats the Q), so modifying data is outside its scope.
That said, your DataGridView is presumably bound to an ItemsSource, perhaps of type ObservableCollection<T> or similar. In that case, just do something like X.ToList().ForEach(yourGridSource.Add) (this might have to be adapted based on the type of source in your grid).
you shoud do like this form your code
DataGridView.DataSource = yourlist;
DataGridView.DataBind();
Related
I am trying to set a value for DataGridViewRow.Tag when data binding but I don't know how to do it?
I tried with DataRow row = table.NewRow(); But row doesn't have tag.
How to set a value for DataGridViewRow.Tag when binding DataGridView to table (for example)? Or it isn't possible?
Edit1:
Here is the code i am using:
var table = new DataTable();
table.Columns.Add("Title", typeof(string));
table.Columns.Add("URL", typeof(string));
table.Columns.Add("Read Later", typeof(bool));
foreach (XElement node in nodes)
{
Helper.CheckNode(node);
var id = node.Attribute("id").Value;
var url = node.Element("path").Value;
var comment = node.Element("title").Value;
var readlater = node.Attribute("readLater")?.Value.ToString() == "1";
var row = table.NewRow();
row.ItemArray = new object[] { url, comment, readlater };
table.Rows.Add(row);//Edit2
}
dataGridView1.DataSource = table;
I am trying to set a tag for the row to use it in CellClick event:
var cRow = dataGridView1.Rows[e.RowIndex];
var id = cRow.Tag.ToString();
Separate the data from how it is displayed
When using a DataGridView, it is seldom a good idea to access the cells and the columns directly. It is way more easier to use DataGridView.DataSource.
In modern programming there is a tendency to separate your data (= model) from the way your data is displayed (= view). To glue these two items together an adapter class is needed, which is usually called the viewmodel. Abbreviated these three items are called MVVM.
Use DataGridView.DataSource to display the data
Apparently, if the operator clicks a cell, you want to read the value of the tag of the row of the cell, to get some extra information, some Id.
This displayed row is the display of some data. Apparently part of the functionality of this data is access to this Id. You should not put this information in the view, you should put it in the model.
class MyWebPage // TODO: invent proper identifier
{
public int Id {get; set;}
public string Title {get; set;}
public string Url {get; set;}
public bool ReadLater {get; set;}
... // other properties
}
Apparently you have a method to fetch the data that you want to display from a sequence of nodes. Separate fetching this data (=model) from displaying it (= view):
IEnumerable<MyWebPage> FetchWebPages(...)
{
...
foreach (XElement node in nodes)
{
Helper.CheckNode(node);
bool readLater = this.CreateReadLater(node);
yield return new MyWebPage
{
Id = node.Attribute("id").Value,
Url = node.Element("path").Value,
Title = node.Element("title").Value,
ReadLater = this.CreateReadLater(node),
};
}
}
I don't know what is in node "ReadLater", apparently you know how to convert it to a Boolean.
bool CreateReadLater(XElement node)
{
// TODO: implement, if null return true; if not null ...
// out of scope of this question
}
For every property that you want to display you create a DataGridViewColumn. Property DataPropertyName defines which property should be shown in the column. Use DefaultCellStyle if a standard ToString is not enough to display the value properly, for instance, to define the number of digits after the decimal point, or to color negative values red.
You can do this using the visual studio designer, or you can do this in the constructor:
public MyForm
{
InitializeComponents();
this.dataGridViewColumnTitle.DataPropertyName = nameof(MyWebPage.Title);
this.dataGridViewColumnUrl.DataPropertyName = nameof(MyWebPage.Url);
...
}
You don't want to display the Id, so there is no column for this.
Now to display the data, all you have to do is assign the list to the datasource:
this.dataGrieViewWebPages.DataSource = this.FetchWebPages().ToList();
This is display only. If the operator can change the displayed values, and you want to access the changed values, you should put the items in an object that implements interface IBindingList, for instance, using class (surprise!) BindingList<T>:
private BindingList<MyWebPage> DisplayedWebPages
{
get => (BindingList<MyWebPage>)this.dataGrieViewWebPages.DataSource;
set => this.dataGrieViewWebPages.DataSource = value;
}
Initialization:
private void DisplayWebPages()
{
this.DisplayedWebPages = new BindingList<MyWebPage>(this.FetchWebPages.ToList());
}
And presto! All webpages are displayed. Every change that the operator makes: add / remove / edit rows are automatically updated in the DisplayedWebPages.
If you want to access the currently selected WebPages:
private MyWebPage CurrentWebPage =>(MyWebPage)this.dataGrieViewWebPages.CurrentRow?.DataBoundItem;
private IEnumerable<MyWebPage> SelectedWebPages =>
this.dataGrieViewWebPages.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<MyWebPage>();
Now apparently whenever the operator clicks a cell, you want to do something with the Id of the WebPage that is displayed in the Row of the cell.
View: Displayed Cell and Row
ViewModel: React when operator clicks a cell
Model Action that must be done
React on Cell Click: get the Id
We've handled the View above. ViewModel is the event handler:
void OnDatGridViewCellClicked(object sender, DataGridViewCellEventArgs e)
{
// use the eventArgs to fetch the row, and thus the WebPage:
MyWebPage webPage = (MyWebPage)this.dataGridViewWebPages.Rows[e.RowIndow].DataBoundItem;
this.ProcessWebPage(webPage);
}
ProcessWebPage is typically a method in your Model class:
public void ProcessWebPage(MyWebPage webPage)
{
// Do what you need to do if the operator clicks the cell, for example:
int webPageId = webPage.Id;
...
}
Conclusion: advantages of separating model from view
By the way, did you see that all ViewModel methods are one-liners? Only your Model methods FetchWebPages and ProcessWebPage contain several lines.
Because you separated the View from the Model, changes to your Model or your View will be fairly simple:
If you want to store your data in Json format, or in a database instead of in an XML, your View won't change
If you don't want to react on cell click, but on Button OK click, then your Model won't change. Your model also doesn't have to change if you decide to show more or less columns
Because you separated your Model from your View, the Model can be unit tested without a form. You can also test the View with a Model filled with only test values.
Im writing program that creates dynamic DataGrid based on user input, user only provides column names,row names, based on that I need to generate empty DataGrid that allows user to enter value.But since I can't find anything for my case, My columns will contain numbers and I can't create type to hold them. The solutions I found only work if you create type and then use properties of that type, but I can't predict how much properties would I need so I would kinda need to add array as row.
What I want to achieve is something like this:
private void generate_DataGrid(string[] headers)
{
foreach (string h in headers) {
DataGridTextColumn gridColumn = new DataGridTextColumn();
gridColumn.Header = h;
tabelaDataGrid.Columns.Add(gridColumn);
}
int[] row1 = new int[3];
int[] row2 = new int[3];
tabelaDataGrid.Items.Add(row1);
tabelaDataGrid.Items.Add(row2);
}
The solution above will generate empty row, but I can't edit them:
"System.InvalidOperationException: ''EditItem' is not allowed for this view.'"
Or maybe should I search for some other solution like spreadsheet library?
Did you try to bind the DataGrid to a DataTable and then just adding Rows and Columns like this: (Please note BindableBase is part of the Prism framework; if you don't use it search for INotifyPropertyChanged Interface to see how to implement it from scratch)
public class DataGridExampleViewModel : BindableBase
{
private DataTable _dataTable;
public DataTable DataTable
{
get { return _dataTable; }
set { SetProperty(ref _dataTable, value); }
}
public DataGridExampleViewModel()
{
//Create a empty dataTable
DataTable = new DataTable();
}
//Add a column to the DataTable the Type is optional
public void AddColumn(string columnName, Type dataType)
{
DataTable.Columns.Add(columnName, dataType);
}
//Add an empty row to the DataTable
public void AddRow()
{
//Create a new row
var row = DataTable.NewRow();
//Add the row to the DataTable
DataTable.Rows.Add(row);
}
}
If you are new to WPF and not familiar with data binding i strongly recommend you to look for it.
Side note:
Check out Prism & MahApps.Metro both are great libraries for building WPF apps.
I've been struggling with this for days, and I'm getting DataTables and dataGridViews all mixed up.
I have a WinForms program, which has a DataGridView, dataGridView1, and a DataTable, errors.
public static DataTable errors = new DataTable();
dataGridView1.DataSource = errors;
Now, further down, in a method called ValidateText, I read data from a text file, line by line, into an array, where I also define the columns for the errors datatable:
errors.Columns.Add("Account Number");
errors.Columns.Add("Customer Name");
errors.Columns.Add("Country");
errors.Columns.Add("State");
errors.Columns.Add("Ship-to Country");
errors.Columns.Add("Ship-to State");
var lines = File.ReadAllLines(file);
foreach (string line in lines)
{.
.
.
string []items=line.Split('\t').ToArray();
errors.Rows.Add(items[0], items[1],...items[5]);
And that works just fine when I run it. the thing is, I want to make the "Country" column have a combo box in it, so when the program is run, and the data is displayed in the dataGridView1, the user will have the opportunity to select a new country, if they want, from the "Countries" column. And further down in the program, I've indeed defined a
method that creates a DataGridViewComboBoxColumn
private DataGridViewComboBoxColumn CreateComboBoxColumn()
{
DataGridViewComboBoxColumn buildCountries = new DataGridViewComboBoxColumn();
buildCountries.HeaderText = "List of Countries";
DataTable dataTable = new DataTable();
dataTable.Columns.Add("Keys");
dataTable.Columns.Add("Values");
KeyValuePair<string, string>[] array = CountryList.ToArray();
foreach (KeyValuePair<string, string> kvp in array)
{
dataTable.Rows.Add(kvp.Key, kvp.Value);
}
buildCountries.DataSource = dataTable;
buildCountries.DisplayMember = "Values";
buildCountries.ValueMember = "Keys";
return buildCountries;
}
The thing that I am having problems with, is how do I get that combo box that I've created in that last method, into the "Countries" column I've created above? I feel like I'm not getting something with DataGridView and DataTable. You bind a DataTable to a DataGridView, but adding this DataGridViewComboBoxColumn seems to be hard.
Thanks,
Amanda
this is just my suggestion, the .NET Framework had a already list of countries. so you can use this class. i forgot who created this code :)
public static class CountryEntries
{
public static IEnumerable<Country> GetCountries()
{
return from ri in
from ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)
select new RegionInfo(ci.LCID)
orderby ri.DisplayName
group ri by ri.TwoLetterISORegionName into g
select new Country
{
CountryId = g.Key,
Title = g.First().DisplayName
};
}
public class Country
{
public string CountryId { get; set; }
public string Title { get; set; }
}
}
In my application i use LookUpEdit to bound data collection.
List<myClass> lst=new List<myClass>();
void Init()
{
lst.Add(new myClass("One"));
LookUpEdit.DataSource=lst;
}
class myClass()
{
public myClass(string name)
{
Name=name;
}
public ovveride ToString()
{
return Name;
}
}
I choose in LookUpEdit item "One" and then delete this item from collection lst. But in LookUpEdit item "One" is shown yet. What is a way to do, that deleted item doesn't show?
Try using the BindingList, which supports those "events", instead of List:
using System.ComponentModel;
BindingList<MyClass> lst = new BindingList<MyClass>();
When you delete the Row from the DataTable containing all the LookUpEdit data, you have to then accept changes to apply the deletion. Example assuming your DataSource is a DataTable:
int row = 5; // ROW TO DELETE
BindingSource binding = lookUpEdit1.Properties.DataSource as BindingSource;
DataTable dt = (binding.DataSource as DataSet).Tables[binding.DataMember];
dt.Rows.RemoveAt(row);
dt.AcceptChanges();
I have a "populate combobox", and I'm so happy with it that I've even started using more comboboxes. It takes the combobox object by reference with the ID of the "value set" (or whatever you want to call it) from a table and adds the items and their respective values (which differ) and does the job.
I've recently had the brilliant idea of using comboboxes in a gridview, and I was happy to notice that it worked JUST LIKE a single combobox, but populating all the comboboxes in the given column at the same time.
ObjComboBox.Items.Add("yadayada");
//works just like
ObjComboBoxColumn.Items.Add("blablabla");
But When I started planning how to populate these comboboxes I've noticed: There's no "Values" property in ComboBoxDataColumn.
ObjComboBox.Values = whateverArray;
//works, but the following doesn't
ObjComboBoxColumn.Values = whateverArray;
Questions:
0 - How do I populate it's values ? (I suspect it's just as simple, but uses another name)
1 - If it works just like a combobox, what's the explanation for not having this attribute ?
-----[EDIT]------
So I've checked out Charles' quote, and I've figured I had to change my way of populating these bad boys. Instead of looping through the strings and inserting them one by one in the combobox, I should grab the fields I want to populate in a table, and set one column of the table as the "value", and other one as the "display". So I've done this:
ObjComboBoxColumn.DataSource = DTConfig; //Double checked, guaranteed to be populated
ObjComboBoxColumn.ValueMember = "Code";
ObjComboBoxColumn.DisplayMember = "Description";
But nothing happens, if I use the same object as so:
ObjComboBoxColumn.Items.Add("StackOverflow");
It is added.
There is no DataBind() function.
It finds the two columns, and that's guaranteed ("Code" and "Description") and if I change their names to nonexistant ones it gives me an exception, so that's a good sign.
-----[EDIT]------
I have a table in SQL Server that is something like
code | text
—————
1 | foo
2 | bar
It's simple, and with other comboboxes (outside of gridviews) i've successfully populated looping through the rows and adding the texts:
ObjComboBox.Items.Add(MyDataTable.Rows[I]["MyColumnName"].ToString());
And getting every value, adding it into an array, and setting it like:
ObjComboBox.Values = MyArray;
I'd like to populate my comboboxColumns just as simply as I do with comboboxes.
I don't mean to sound obnoxious, but do you know there's documentation for all this stuff?
From http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridviewcomboboxcolumn.aspx:
You can populate the column drop-down
list manually by adding values to the
Items collection. Alternatively, you
can bind the drop-down list to its own
data source by setting the column
DataSource property. If the values are
objects in a collection or records in
a database table, you must also set
the DisplayMember and ValueMember
properties. The DisplayMember property
indicates which object property or
database column provides the values
that are displayed in the drop-down
list. The ValueMember property
indicates which object property or
database column is used to set the
cell Value property.
EDIT:
From your edit, it sounds like you might be trying to use non-public properties of the underlying type for DisplayMember and/or ValueMember. Or if your combobox datasource is a DataTable, make sure it has "Code" and "Description" columns.
Here's a simple demo. I create a list of Foo's and assign it as the DataSource of my combobox column. Just create a winforms application and paste this in.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// this will be the datasource for the combo box column; you could also bind it to a dataset
List<Foo> foos = new List<Foo>() {
new Foo() { FooID = 0, FooName = "No Foo." },
new Foo() { FooID = 1, FooName = "Foo Me Once" },
new Foo() { FooID = 2, FooName = "Foo Me Twice" },
new Foo() { FooID = 3, FooName = "Pity The Foo!" }
};
DataGridView dataGridView1 = new DataGridView();
dataGridView1.AutoGenerateColumns = false;
// add normal text column
DataGridViewColumn column = new DataGridViewTextBoxColumn();
column.DataPropertyName = "MyText";
column.Name = "Text";
dataGridView1.Columns.Add(column);
// add the combo box column
DataGridViewComboBoxColumn comboCol = new DataGridViewComboBoxColumn();
comboCol.Name = "Foo";
// bind it to the list of foos to populate it
comboCol.DataSource = foos;
// specify which property of the grid's datasource to bind
comboCol.DataPropertyName = "MyFoo";
// specify the property of the combo's datasource to bind
comboCol.ValueMember = "FooID";
// specify the property of the combo's datasource to display
comboCol.DisplayMember = "FooName";
dataGridView1.Columns.Add(comboCol);
// add some data
BindingSource bindingSource1 = new BindingSource();
bindingSource1.Add(new BusinessObject(1, "You say"));
bindingSource1.Add(new BusinessObject(2, "George says"));
bindingSource1.Add(new BusinessObject(3, "Mr. T says"));
bindingSource1.Add(new BusinessObject());
dataGridView1.DataSource = bindingSource1;
Controls.Add(dataGridView1);
dataGridView1.Dock = DockStyle.Fill;
}
class Foo
{
public int FooID { get; set; }
public string FooName { get; set; }
}
class BusinessObject
{
public BusinessObject(int foo, string text)
{
MyFoo = foo;
MyText = text;
}
public BusinessObject()
{
MyFoo = 0;
MyText = "";
}
public string MyText { get; set; }
public int MyFoo { get; set; }
}
}