Thursday, September 20, 2007

Linq with Xceed's WPF Grid - Part II

In my first post I showed a handy binding class that wraps around your linq data tables, now I'm going to show some handy changes to Xceed's data grid, to complete the linq binding.

My main problem with the data grid, is there isn't any good place to commit a transaction after editing a row.

To fix this, we can provide our own DataRow class which handles the SubmitChanges() when a row is dirty.

Update: Modified code to use IsDirty flag. Note that IsDirty is reset to false in base.EndEdit().


class LinqRow : DataRow
{
public override void EndEdit()
{
if (IsDirty == true)
DataSource.Context.SubmitChanges();

base.EndEdit();
}
}


To make the DataGridControl aware of our new DataRow class, we need to derive and override the GetContainerForItemOverride() method.

class LinqGrid : DataGridControl
{
protected override DependencyObject
GetContainerForItemOverride()
{
return new LinqRow();
}

protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);

if (View == null)
{
View = new TableView();
AdjustHeadersFooters();
}
}

private void AdjustHeadersFooters()
{
// Show / hide the element base
View.FixedHeaders.Clear();
View.Headers.Clear();

// Add a ColumnManagerRow
DataTemplate columnTemplate = new DataTemplate();
columnTemplate.VisualTree =
new FrameworkElementFactory(typeof(ColumnManagerRow));
View.FixedHeaders.Add(columnTemplate);

// Add the insertion row
DataTemplate insertTemplate = new DataTemplate();
insertTemplate.VisualTree =
new FrameworkElementFactory(typeof(InsertionRow));
View.FixedHeaders.Add(insertTemplate);
}
}



Insertion Row

You may have noticed the AdjustHeadersFooters call in the class above, this code removes the GroupBy control and adds in the Insertion row by default. The GroupBy control is cool, but I rarely use it.

Putting it together

Instead of using the DataGridControl in your xaml file, simply use the LinqGrid.

In my next few posts I'll discuss hooking up the delete, sorting and filtering, and how to use the linq queries instead of the whole table.

3 comments:

Marcus said...

Hi Luke,

I like where you are going with this series of post...

I do have 2 suggestions on the design at this point:

- Instead of declaring your own "dirty" flag, I suggest using the DataRow.IsDirty property. The main advantage of using it will be that if the LinqRow enters edition mode but receives no modifications, then changes will not be submitted (can dramatically reduce number of calls being made, depending on your EditTriggers/CellEditorDisplayConditions )

- You could add the Linq context instance as a property on the LinqGrid instead of using a static reference. This would make the design more versatile and re-usable.

Accessing the context would then be as easy as:

LinqGrid parentDataGrid = DataGridControl.GetParentDataGridControl( this ) as LinqGrid;

if( parentDataGrid != null)
{
parentDataGrid.LinqContext.SubmitChanges();
}

I'm already impatient to see your next posts of this series.

Luke Marshall said...

Thanks Marcus! They are good suggestions.

I originally tried using the DataRow.IsDirty flag, but it is read-only and for some reason wasn't being set correctly.

I was going to look into this further as I totally agree, if nothing has been changed, there's no point in calling SubmitChanges().

The flip side is that linq handles all of the changes, so if nothing has changed, SubmitChanges() does nothing.


I like the property idea for the LinqGrid, and will definately use it. I'll still probably set it to use the singleton, (as linq doesn't play well with multiple instances) - but the design will be much cleaner.

Cheers!

Luke Marshall said...

I've updated the post again to remove the need for the dirty flag hack.

Embarrassingly the base.EndEdit() call was reseting the flag to false.

I hang my head in shame. :)

Cheers,
Luke