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; }
}
Related
private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
List<ReportList> alstNames = new List<ReportList>();
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
DataTable dt = new DataTable();
dt.Columns.Add("Particulars", typeof(string));
dt.Columns.Add("Amount", typeof(string));
dt.Columns.Add("Particulars1", typeof(string));
dt.Columns.Add("Amount1", typeof(string));
foreach (var lstNames in alstNames)
{
// Add new Row - inside the foreach loop - to enable creating new row for each object.
DataRow row = dt.NewRow();
row["Amount"] = fItem;
row["Particulars"] = sItem;
row["Particulars1"] = tItem;
row["Amount1"] = foItem;
dt.Rows.Add(row);
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}
}
This returns only the most recent row added and ignores the others. How do I create an array from parameters passed to a method and bind them to a gridview?
Your code does not look good. First, you are doing a loop over the alstNames and binding it into a gridview over each iteration of this loop. Second, you do not need a DataTable to bind it into a grid and your code you are just creating a DataTable on the heap, adding some Rows and keep it to Garbage Collector remove it, you are not even using it. Third, considering you are doing a method to add a new item on the GRidView, remove the initilization of your list to the scope of your class and keep it a valid instance (or static one if it is the case):
// declare the list output of the method
private private List<ReportList> alstNames = new List<ReportList>();
private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
// add the ReportList on the list
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
// bind it on the gridView
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}
I have a method as such :
public void setResults(Documents[] doc)
{
//I call a method to set the columns
setColumns(doc[0].getColumnsToDisplay())
//Here i populate the DataGrid
foreach (Documents element in doc)
{
/dt is a datatable
DataRow dr = dt.NewRow();
foreach (string col in results[0].getColumnsToDisplay())
{
object val;
(element.getAttributes() as IDictionary<string, object>).TryGetValue(col, out val);
dr[col] = val.ToString();
}
dt.Rows.Add(dr);
}
gridSearchResults.DataSource = dt;
}
Is there a way to store "doc" reference on each row ? so that if i select a row, i can be able to return the "doc" object ? Looking for some kind of Tag property alternative for rows or something different.
When you want to show a list of your objects in a DataGridView, you don't need to use a DataTable. You can simply set your list as DataSource of your DataGridView:
public void setResults(Documents[] doc)
{
gridSearchResults.DataSource = doc;
}
Then if you want to know the object behind the row, you can simply use DataBoundItem property of the DataGridViewRow and cast it to your object type:
var documents= (Documents)gridSearchResults.Rows[0].DataBoundItem;
For more information and samples, you can see:
How to: Access Objects Bound to Windows Forms DataGridView Rows.
If you want to show only some columns in the DataGridView, add the columns you need this way:
this.dataGridView1.Columns.Clear();
this.dataGridView1.AutoGenerateColumns = false;
//Create Column
var column1 = new DataGridViewTextBoxColumn()
{
Name = "firstNameColumn", /*Name of Column*/
HeaderText = "First Name", /*Title of Column*/
DataPropertyName = "FirstName" /*Name of the property to bind to cilumn*/
};
//Add column to grid
this.dataGridView1.Columns.Add(column1);
You should use DataGridView instead of DataGrid. I assume you're trying to get the object after postback.
//DO NECESSARY NULL CHECKS!!!
DataRow row = (gridSearchResults.SelectedRows[0].DataBoundItem as DataRowView).Row;
Documents selected = (Documents)row;
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; }
}
}
hey guys,Actually i have two comboboxes having same elements but i have used two datasets having same elements.Now when i bind combobox with dataset ,it repeats its elements.
Can anyone help me to sort it out?
my code goes like this:
DataSet ds_PromotionDesignation = new DataSet();
ds_PromotionDesignation = EPI.comboDeg();
cmbPromotionDesignationFrom.DataSource = ds_PromotionDesignation.Tables[0];
cmbPromotionDesignationFrom.DisplayMember = "DEG_NAME";
cmbPromotionDesignationFrom.ValueMember = "DEG_ID";
cmbPromotionDesignationFrom.SelectedIndex = -1;
DataSet ds_PromotionDesignationTo = new DataSet();
ds_PromotionDesignationTo = EPI.PromotionDesignationTo();
foreach (DataRow row in ds_PromotionDesignationTo.Tables["tbl_org_Desg"].Rows)
{
myAL.Add(new USState(row["DEG_ID"].ToString(),row["DEG_NAME"].ToString()));
}
cmbPromotionDesignationTo.DataSource = myAL;
cmbPromotionDesignationTo.DisplayMember = "DEGNAME";
cmbPromotionDesignationTo.ValueMember = "DEGID";
Looking at your code it looks like you want to transfer ownership of an item from one guy to another, using two combos that both populate the same list of data.
You want to exclude the selected item form the destination combo. Try in the foreach loop, to only add an item if it is not the same as the current selected value, something like this (untested)
foreach (DataRow row in ds_PromotionDesignationTo.Tables["tbl_org_Desg"].Rows)
{
if ((int)row["DEG_ID"] != (int)cmbPromotionDesignationFrom.SelectedValue)
{
myAL.Add(new USState(row["DEG_ID"].ToString(), row["DEG_NAME"].ToString()));
}
}
I want to know how should we add columns and rows programmatically to a DataGrid in WPF. The way we used to do it in windows forms. create table columns and rows, and bind it to DataGrid.
I have No. of rows and columns which I need to draw in DataGrid so that user can edit the data in the cells.
To programatically add a row:
DataGrid.Items.Add(new DataItem());
To programatically add a column:
DataGridTextColumn textColumn = new DataGridTextColumn();
textColumn.Header = "First Name";
textColumn.Binding = new Binding("FirstName");
dataGrid.Columns.Add(textColumn);
Check out this post on the WPF DataGrid discussion board for more information.
I had the same problem. Adding new rows to WPF DataGrid requires a trick. DataGrid relies on property fields of an item object. ExpandoObject enables to add new properties dynamically. The code below explains how to do it:
// using System.Dynamic;
DataGrid dataGrid;
string[] labels = new string[] { "Column 0", "Column 1", "Column 2" };
foreach (string label in labels)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = label;
column.Binding = new Binding(label.Replace(' ', '_'));
dataGrid.Columns.Add(column);
}
int[] values = new int[] { 0, 1, 2 };
dynamic row = new ExpandoObject();
for (int i = 0; i < labels.Length; i++)
((IDictionary<String, Object>)row)[labels[i].Replace(' ', '_')] = values[i];
dataGrid.Items.Add(row);
//edit:
Note that this is not the way how the component should be used, however, it simplifies a lot if you have only programmatically generated data (eg. in my case: a sequence of features and neural network output).
try this , it works 100 % : add columns and rows programatically :
you need to create item class at first :
public class Item
{
public int Num { get; set; }
public string Start { get; set; }
public string Finich { get; set; }
}
private void generate_columns()
{
DataGridTextColumn c1 = new DataGridTextColumn();
c1.Header = "Num";
c1.Binding = new Binding("Num");
c1.Width = 110;
dataGrid1.Columns.Add(c1);
DataGridTextColumn c2 = new DataGridTextColumn();
c2.Header = "Start";
c2.Width = 110;
c2.Binding = new Binding("Start");
dataGrid1.Columns.Add(c2);
DataGridTextColumn c3 = new DataGridTextColumn();
c3.Header = "Finich";
c3.Width = 110;
c3.Binding = new Binding("Finich");
dataGrid1.Columns.Add(c3);
dataGrid1.Items.Add(new Item() { Num = 1, Start = "2012, 8, 15", Finich = "2012, 9, 15" });
dataGrid1.Items.Add(new Item() { Num = 2, Start = "2012, 12, 15", Finich = "2013, 2, 1" });
dataGrid1.Items.Add(new Item() { Num = 3, Start = "2012, 8, 1", Finich = "2012, 11, 15" });
}
I found a solution that adds columns at runtime, and binds to a DataTable.
How do I bind a WPF DataGrid to a variable number of columns?
Unfortunately, with 47 columns defined this way, it doesn't bind to the data fast enough for me. Any suggestions?
xaml
<DataGrid
Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding}">
</DataGrid>
xaml.cs
using System.Windows.Data;
if (table != null) // table is a DataTable
{
foreach (DataColumn col in table.Columns)
{
dataGrid.Columns.Add(
new DataGridTextColumn
{
Header = col.ColumnName,
Binding = new Binding(string.Format("[{0}]", col.ColumnName))
});
}
dataGrid.DataContext = table;
}
edit: sorry, I no longer have the code mentioned below. It was a neat solution, although complex.
I posted a sample project describing how to use PropertyDescriptor and lambda delegates with dynamic ObservableCollection and DynamicObject to populate a grid with strongly-typed column definitions.
Columns can be added/removed at runtime dynamically. If your data is not a object with known type, you could create a data structure that would enable access by any number of columns and specify a PropertyDescriptor for each "column".
For example:
IList<string> ColumnNames { get; set; }
//dict.key is column name, dict.value is value
Dictionary<string, string> Rows { get; set; }
You can define columns this way:
var descriptors= new List<PropertyDescriptor>();
//retrieve column name from preprepared list or retrieve from one of the items in dictionary
foreach(var columnName in ColumnNames)
descriptors.Add(new DynamicPropertyDescriptor<Dictionary, string>(ColumnName, x => x[columnName]))
MyItemsCollection = new DynamicDataGridSource(Rows, descriptors)
Or even better, in case of some real objects
public class User
{
public string FirstName { get; set; }
public string LastName{ get; set; }
...
}
You can specify columns strongly typed (related to your data model):
var propertyDescriptors = new List<PropertyDescriptor>
{
new DynamicPropertyDescriptor<User, string>("First name", x => x.FirstName ),
new DynamicPropertyDescriptor<User, string>("Last name", x => x.LastName ),
...
}
var users = retrieve some users
Users = new DynamicDataGridSource<User>(users, propertyDescriptors, PropertyChangedListeningMode.Handler);
Then you just bind to Users collections and columns are autogenerated as you speficy them. Strings passed to property descriptors are names for column headers. At runtime you can add more PropertyDescriptors to 'Users' add another column to the grid.
If you already have the databinding in place John Myczek answer is complete.
If not you have at least 2 options I know of if you want to specify the source of your data. (However I am not sure whether or not this is in
line with most guidelines, like MVVM)
option 1: like JohnB said. But I think you should use your own defined collection
instead of a weakly typed DataTable (no offense, but you can't tell from the
code what each column represents)
xaml.cs
DataContext = myCollection;
//myCollection is a `ICollection<YourType>` preferably
`ObservableCollection<YourType>
- option 2) Declare the name of the Datagrid in xaml
<WpfToolkit:DataGrid Name=dataGrid}>
in xaml.cs
CollectionView myCollectionView =
(CollectionView)CollectionViewSource.GetDefaultView(yourCollection);
dataGrid.ItemsSource = myCollectionView;
If your type has a property FirstName defined, you can then do what John Myczek pointed out.
DataGridTextColumn textColumn = new DataGridTextColumn();
dataColumn.Header = "First Name";
dataColumn.Binding = new Binding("FirstName");
dataGrid.Columns.Add(textColumn);
This obviously doesn't work if you don't know properties you will need to show in your dataGrid, but if that is the case you will have more problems to deal with, and I believe that's out of scope here.
If you already have the databinding in place John Myczek answer is complete.
If not you have at least 2 options I know of if you want to specify the source of your data. (However I am not sure whether or not this is in line with most guidelines, like MVVM)
Then you just bind to Users collections and columns are autogenerated as you speficy them. Strings passed to property descriptors are names for column headers. At runtime you can add more PropertyDescriptors to 'Users' add another column to the grid.
To Bind the DataTable into the DataGridTextColumn in CodeBehind
xaml
<DataGrid
Name="TrkDataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding}">
</DataGrid>
xaml.cs
foreach (DataColumn col in dt.Columns)
{
TrkDataGrid.Columns.Add(
new DataGridTextColumn
{
Header = col.ColumnName,
Binding = new Binding(string.Format("[{0}]", col.ColumnName))
});
}
TrkDataGrid.ItemsSource= dt.DefaultView;