Adding Column Dynamically to a Datagrid - wpf

I am working on WPF datagrid. I am using auto-generated columns as I am supposed to retrieve data from excel sheet and number of columns are not fixed.
On the celleditending event I am checking whether it is the last column or not. if it is I am adding the new column to datatable and I refresh the ItemsSource of the grid as mentioned below:
private void grdEmployee_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if ((e.Column.DisplayIndex + 1) == empDS.Tables[0].Columns.Count)
{
DataColumn col = new DataColumn((e.Column.DisplayIndex + 1).ToString());
empDS.Tables[0].Columns.Add(col);
grdEmployee.ItemsSource = null;
grdEmployee.ItemsSource = empDS.Tables[0].DefaultView;
grdEmployee.Items.Refresh();
}
}
The problem I am facing is, when I refresh the itemssource, I am losing the data from row which user was editing. Data entered by user is committed to datatable only when user finishes row editing.
Please guide me. If you need any further info please let me know.
Regards,
Priyank

Related

Manually adding a new row to DataGridView does not immediately update the bound DataTable

I have a c# windows form app written in .net-4.0
I have a datagridview (dgvItems) which I programmatically bind to a dataset and datatable at the end of an import function.
I have an import button that imports data from a selected excel file into a table called _dtItemAdjustList, once the data has been imported I bind that table to my grid. The code I use to bind the grid is:
dgvItems.DataSource = _dsQtyAdjust;
dgvItems.DataMember = "Item Adjust List";
dgvItems.Refresh();
Everything works fine so far, if I edit the imported data in the datagridview, it updates the bound table _dtItemAdjustList after each cell is edited.
My issue comes in when I try to manually add a row to my datagridview, it doesn't immediately add that new row to the bound datatable.
Example: I put a break point at the end of my dgvItems_CellValueChanged event and added a _dtItemAdjustList.Rows.Count watch and here is what happens.
After the data is imported and I edit one of the existing imported lines the watch shows the correct row count, lets say 5.
Now I click the last * row, and type something into my item# column and hit tab, the break point fires but my row count still only shows 5, I fill out a few more cells in the new row but each time I leave a cell and my CellValueChanged event fires and the row count remains at 5.
Next I add a second manual row, now immediately after I tab out of the first cell I filled out for this second new row, my row counter goes to 6, but technically at this point I've added 2 manual rows to my imported 5, so the counter should read 7
This repeats, basically each new manually added row to the datagridview isn't added to the bound datatable until either a) another row is added or b) I go back and re-edit a cell on an existing row.
Edit
Forgot to mention I tried binding my DataGridView two ways:
DataSource = _dsQtyAdjust //dataset Name
DataMember = "Item Adjust List" // Table Name in the dataset
DataSource = _dtItemAdjustList
It made no difference as far as my new row behaviour goes.
Ok after a lot of trial and error and online searching I found a solution that seems to work.
Instead of binding my DataGridView directly to my DataTable, I created a BindingSource, bound the BindingSource to my DataTable, then bound my DataGridView to the BindingSource and finally in my dgvItems_CellValueChanged event I used a combination of EndEdit() and NotifyCurrentCellDirty(true/false) to make it work.
Code sample:
public partial class frmQuantityAdjustment : Form
{
// In my forms partial class I created a new public BindingSource
public BindingSource bsItemAdjust = new BindingSource();
}
private void frmQuantityAdjustment_Load(object sender, EventArgs e)
{
// In my form_Load event I bound the BindingSource to my DataTable
// and then the DataGridView to the BindingSource
bsItemAdjust.DataSource = _dtItemAdjustList;
dgvItems.DataSource = bsItemAdjust;
dgvItems.Refresh();
}
private void dgvItems_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// Finally at the end of my CellValueChanged event I added the
// following code
bsItemAdjust.EndEdit();
dgvItems.NotifyCurrentCellDirty(true);
dgvItems.EndEdit();
dgvItems.NotifyCurrentCellDirty(false);
}
I came across this solution on this thread and after some testing it seems to work perfectly.
Now the new row in my DataGridView is added to the bound DataTable at the end of the CellValueChanged event.

Automatically create an input UI from db fields in winforms?

I'm trying to create a form in winforms to add records to a db, a new customer for example. I'm working with entity framework.
What I did until today is to create a new empty "Customer" object from the class that the entity framework generated. Then I added this empty object to a list and set the list as the datasource of a datagridview.
That way I automatically had in the grid all the required fields to input to the db.
Everything worked.
But now, the client wants a better design for the UI - something that looks like a contact form in web pages and not a grid row.
How can I make something like that automatically, like I had with the datagridview, creating all the input fields automatically according to the db structure without creating manually labels and textboxes?
Your best bet would be to keep the DataGridView but override the style so that it looks far from a grid and more like what your boss is expecting.
Some suggestions to achieve this:
Remove the lines between each row.
Remove the headers and grid borders.
Add lots of padding to each row and column so each entry is spaced
out. For more advanced stuff you may need to override the Paint
method of some of the controls of the grid.
I ended up iterating the grid and creating textboxes and labels in each iteration.
void Generate_TextBoxes()
{
// top of textboxes
int current_top=150;
int current_left = 1000;
// index used to match between each textbox and the properate column in grid
int my_index = -1;
// iterate the grid and create textbox for each column
foreach (DataGridViewColumn col in dataGridView_add_customer.Columns)
{
my_index++;
// generate textboxes only for visible columns
if (col.Visible == true)
{
// increase the top each time for space between textboxes
current_top += 40;
// create a second column of textboxes (not all of them in 1 long column)
if (my_index == 6) { current_top = 190; current_left = 450; }
TextBox t = new TextBox();
t.Top = current_top;
t.Left = current_left;
t.Width = 170;
t.TextChanged +=new EventHandler(t_TextChanged);
// give an 'id' for each textbox with the corresponding index of the grid
t.Name = my_index.ToString();
Label l = new Label();
l.Text = col.HeaderCell.Value.ToString();
l.Top = current_top;
l.Left = current_left + 190;
this.Controls.Add(l);
this.Controls.Add(t);
}
and the function that binds the textbox to the grid:
void t_TextChanged(object sender, EventArgs e)
{
// create a reference in order to be able to access grid properties such as rows..
TextBox tt = (TextBox)sender;
// access the correct cell in the grid using the Name property you gave to the textbox (it was name=current_index..)
dataGridView_add_customer.Rows[0].Cells[int.Parse(tt.Name)].Value = tt.Text;
}

Perform actions after WPF DataGrid data is completely bound

I have an old Windows Forms application at work which I am converting to WPF. As part of this, there is a button which, when clicked, creates a brand new DataGridView and adds it to the page, binding it to data from a SQL query. This data is bever written back to the database, but a new column is added to the end of the data with a checkbox on it, and when the checkbox is changed the ID from the row is passed into another method along with the state of the checkbox.
In WPF, I have the dynamic grid creation working, and the data binding. Having found I couldn't directly add the column to the DataGrid itself, I've added it to the source DataSet table before binding. This works and whos all the data along with a checkbox for each row. However, I can't get an event to fire when the an individual checkbox is clicked.
I have managed to find some code at http://forums.silverlight.net/t/11547.aspx, which causes the row to commit whenever the checkbox is changed, as my code to pass the ID and bool value currently resides in an EditEnding event. Initially the UpdateSourceTrigger was added to each checkbox by looping over each row in the checkbox column during the "Loaded" event of the DataGrid, but this is now failing, and after some internet searching it seems to be because "Loaded" doesn't guarantee that the data has finished binding, and I'm finding that DataGrid.Items contains more items than there are containers in the grid at the point the event fires. Below is the code I'm trying to use to bind the UpdateTrigger, but at row 32 suddenly the ContainerFromIndex method returns null. There are 69 items in dgvNew.Items at that point.
dgvNew.Loaded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs e)
{
for (var i = 0; i < dgvNew.Columns.Count; i++)
{
DataGridBoundColumn column = dgvNew.Columns[i] as DataGridBoundColumn;
if (column != null && column.Header.ToString() == "HasPermission")
{
for (var j = 0; j < dgvNew.Items.Count; j++)
{
DataGridRow row = (DataGridRow)dgvNew.ItemContainerGenerator.ContainerFromIndex(j);
UpdateSourceTriggerHelper.SetUpdateSourceTrigger(column.GetCellContent(row), true);
}
}
}
});
I've also tried wrapping the for loop up in an event handler for the ItemContainerGenerator.StatusChanged and checking that Status is ItemsGenerated, but that didn't help either.
Can anyone see where I'm going wrong, in code or in understanding?

Can I programmatically add a row to a WPF datagrid?

I just want to add a new row, I have my datasource in objects in which I need to do some processing. i need something like below for wpf datagrid...
DataRow row = dataTable.NewRow();
foreach (NavItem item in record.Items)
{
row[item.FieldNo.ToString()] = item.RecordValue;
}
dataTable.Rows.Add(row);
You should be using an ObservableCollection<NavItem> as the datagrid source. Then simply adding a new element to your collection will add it to the datagrid.
See this MSDN article.
I do not know if it is the right solution, but I came up to something like this, in desperation:
foreach (NavField field in this.Fields)
{
DataGridTextColumn column = new DataGridTextColumn();
column.Header = field.FieldNo.ToString();
//Some other logic
// Hide non active and hidden fields
if (!field.Active || !field.Show)
column.Visibility = System.Windows.Visibility.Collapsed;
grid.Columns.Add(column);
}
Then I add the datatable as itemssource:
this.dataGridLines.ItemsSource = dataTable.DefaultView;
If I set the datatable directly, it does not care about the columns from the datatable and autogenerate its own columns, don't know why..

How to do get this CRUD code to work on WPF DataGrid?

I'm writing CRUD code for the WPF Datagrid.
In the TheDataGrid_CellEditEnding method below:
how do I get the original text before the user made the change?
I need the original text to be able to change the customer and save it back to the database with _db.SubmitChanges()
Here's the full solution with database if anyone wants to experiment with this:
http://www.tanguay.info/web/download/testDataGrid566northwindDatagrid.zip
XAML:
<toolkit:DataGrid x:Name="TheDataGrid"
AutoGenerateColumns="True"
CellEditEnding="TheDataGrid_CellEditEnding"/>
code-behind:
private void TheDataGrid_CellEditEnding(object sender, Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs e)
{
//get the original text
Customer customer = e.Row.Item as Customer;
string customerID = customer.CustomerID;
int displayIndex = (int)e.Column.DisplayIndex; // e.g. equals 4 when user edits the 5th column
//HOW TO I GET THE ORIGINAL TEXT? THERE IS NO FIELDS METHOD IN THE LINQ-TO-SQL CLASSES
string originalText = customer.Fields[displayIndex].value.ToString();
//get the changed text
TextBox changedTextBox = e.EditingElement as TextBox;
string changedText = changedTextBox.Text;
//inform user
Message.Text = String.Format("cell was changed from {0} to {1}", originalText, changedText);
//I NEED TO CHANGE THE CUSTOMER WITH THE ABOVE TEXT
//BEFORE I SAVE IT BACK HERE
_db.SubmitChanges();
}
Why do you need the original text? Is it to display some informational message?
In your case, you seem to be binding the datagrid to your LinqToSQL objects. This means that the Customer object the row is bound to is already updated and all you need to do is call SubmitChanges().
I found the samples in this blog very helpful
http://blogsprajeesh.blogspot.com/2009/03/blog-post.html
It explains sorting using the collection and grid events.

Resources