How to create a combobox column in a datatable - c#

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; }
}
}

Related

C# WPF How to add dynamic row item to DataGrid without binding it to any type?

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.

DataGrid generating blank rows

I am populating a DataTable with some values (after defining the column names) and then using it as a data source for a Sage Grid - functionally very similar to a WinForms DataGridView.
So far, I have tried adding the data type to the DataTable columns and populating a BindingSource with the DataTable and then binding it to the Sage Grid. When viewing the contents of the DataTable whilst debugging you can see the data is there using DataSet Visualiser.
Creating the DataTable -
DataTable failedOrders = new DataTable();
failedOrders.Columns.Add("externalItemCodeColumn", typeof(String));
failedOrders.Columns.Add("reasonColumn", typeof(String));
foreach (String item in insufficientItemsAvailable)
{
DataRow dataRow = failedOrders.NewRow();
dataRow["externalItemCodeColumn"] = item;
dataRow["reasonColumn"] = "Not enough available items in WAREHOUSE";
failedOrders.Rows.Add(dataRow);
}
Populating the Sage Grid -
Sage.Common.Controls.GridColumn externalItemCodeColumn = new Sage.Common.Controls.GridColumn();
externalItemCodeColumn.Caption = "External Item Code";
externalItemCodeColumn.DisplayMember = "externalItemCodeColumn";
Sage.Common.Controls.GridColumn reasonColumn = new Sage.Common.Controls.GridColumn();
reasonColumn.Caption = "Reason";
reasonColumn.DisplayMember = "reasonColumn";
failedOrdersGrid.Columns.Add(externalItemCodeColumn);
failedOrdersGrid.Columns.Add(reasonColumn);
failedOrdersGrid.DataSource = FailedOrders;
//failedOrdersGrid.Refresh(); - this doesn't seem to make a difference
Please note that failedOrders is passed to another method hence the name change from failedOrders to FailedOrders.
Just to check that this behaviour is not specific to the Sage Grid, I tried populating a regular WinForms DGV with the DataTable. (Note - I disabled AutoGenerateColumns as this does not seem to be an option for the Sage Grid).
dataGridView1.AutoGenerateColumns = false;
dataGridView1.Columns.Add("externalItemCodeColumn", "External Item Code");
dataGridView1.Columns.Add("reasonColumn", "Reason");
dataGridView1.DataSource = FailedOrders;
I expect the contents of the Sage Grid to match those of the DataGrid, but instead get blank rows.
The Sage.Common.Controls.Grid inherits from the Sage.Common.Controls.List and the DataSource property of Sage.Common.Controls.List requires on refresh that the new DataSource supports at least IList
(It literally just nulls out your datasource if it isn't an IList)
Here's an adaptation of your code to work with the Sage.Common.Controls.Grid
I added this to the constructor of my form (where failedOrdersGrid is of type Sage.Common.Controls.Grid):
List<Failure> failedOrders = new List<Failure>();
foreach (String item in new List<string> { "1", "2", "3" })
{
failedOrders.Add(new Failure { externalItemCodeColumn = item, reasonColumn = "Not enough available items in WAREHOUSE" });
}
Sage.Common.Controls.GridColumn externalItemCodeColumn = new Sage.Common.Controls.GridColumn();
externalItemCodeColumn.Caption = "External Item Code";
externalItemCodeColumn.DisplayMember = "externalItemCodeColumn";
Sage.Common.Controls.GridColumn reasonColumn = new Sage.Common.Controls.GridColumn();
reasonColumn.Caption = "Reason";
reasonColumn.DisplayMember = "reasonColumn";
failedOrdersGrid.Columns.Add(externalItemCodeColumn);
failedOrdersGrid.Columns.Add(reasonColumn);
failedOrdersGrid.DataSource = failedOrders;
And here's the failure class:
public class Failure
{
public string externalItemCodeColumn { get; set; }
public string reasonColumn { get; set; }
}

Creating a dictionary or ienumerable object

So I have a two columns from a database that will return all the products that are in my store and the department id's that are associated with the product.
What I want to do is create something using a list/dictionary/ienumerable set so that if I give a function a product id, it will spit out the department id. At the moment I am having some troubles getting the declarations correct and need some help in that department.
First I have the base for the relationship between the Product and Category. Then I want ProductCategoryCollection to return the collection of all the mappings for each product and category/department. I am stuck in the second part and not really sure where to go from where I am at.
helper.GetProductToCategoryMatching() returns the rows from the database.
public class ProductAndCategoryID
{
public ProductAndCategoryID(int product, int category)
{
this.productID = product;
this.categoryID = category;
}
public int productID;
public int categoryID;
}
public class ProductCategoryCollection : IEnumerable<ProductAndCategoryID>
{
public ProductCategoryCollection()
{
}
public List<ProductCategoryCollection> populate()
{
ShippingClassHelper helper = new ShippingClassHelper();
DataSet ds = new DataSet();
List<ProductCategoryCollection> list = new List<ProductCategoryCollection>();
ds = helper.GetProductToCategoryMatching();
foreach (DataRow row in ds.Tables[0].Rows)
{
}
return new List<ProductCategoryCollection>();
}
}
All you need to do now is create a ProductCategoryCollection object inside of your loop and add it to your list.
public List<ProductAndCategoryID> populate()
{
ShippingClassHelper helper = new ShippingClassHelper();
DataSet ds = new DataSet();
List<ProductAndCategoryID> list = new List<ProductAndCategoryID>();
ds = helper.GetProductToCategoryMatching();
foreach (DataRow row in ds.Tables[0].Rows)
{
var pc = new ProductAndCategoryID();
pc.ProductID = row[0];
pc.CategoryID = row[1];
list.Add(pc);
}
return list;
}
If I understand your question and your requirements correctly, you would like to get a dictionary that maps a ProductID to a CategoryID, so that a lookup can be performed for the CategoryID of a given ProductID.
If that is a good translation of your problem, this is what you can do:
var productMap = new ShippingClassHelper()
.GetProductToCategoryMatching()
.Tables[0].Rows
.ToDictionary(row => (int)row[0], row => (int)row[1]);
It makes the following assumptions:
The "ProductID" field is an integer and the first field in a row.
The "CategoryID" field is an integer and the second field in a row.
Your dataset does not contain duplicate "ProductID" values.
Now you can use this dictionary to perform lookups. If you want to check if a given product id exists, you can do:
var containsProduct660 = productMap.ContainsKey(660);
If you want to retrieve the category id for a given product id you can do:
var categoryIdForProduct660 = productMap[660];

LookUpEdit show deleted item

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();

How to add data to DataGridView

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();

Categories