Thursday, September 27, 2007

Updatable, inheritable, WPF bounded Linq queries

This is part 4 of my "binding Linq to Xceed's data grid" blog series; however I've changed direction dramatically.

Skipping the details, heres the code binding to a linq query:

users.LinqContext = new UserDataContext();

users.ItemsSource = from i
in users.DataSource<User>()
select i;


And this code fully supports inserts/updates/deletes.

The implementation lives inside the DataSource<User>() call. This devious character returns a reference to a IBindingList (which supports the table modifications) and implements IQueryable (to support linq queries).


What about inheritance?

Imagine you had a sub class called SuperUser; if you've played with linq inheritance you'll know that you have to use the base type User to actually do anything. It's kind of a pain, and you get real friendly with the .OfType<>() call.

So here's the code again, this time selecting all SuperUsers.


users.ItemsSource = from i
in users.DataSource<User, SuperUser>()
select i;


Can I use .Take(x)?

As far as I can tell, all the standard linq statements are supported (including aggregate operators). I haven't looked into partial classes and anonymous types, though I doubt they'd work well.


I won't go into the laborious details of how it's all implemented, just know that it's been fun playing with linq expression trees and writing my own IQueryProvider.

If you are interested, the code can be found here.

I haven't seen anything quite like this out there, so if you have some opinions - I'd love to hear some feedback.

Monday, September 24, 2007

Linq Table and GetNewBindingList()

While I'm talking about binding linq data tables to WPF, I should mention the simplest method, namely Table.GetNewBindingList();

This method allows you to add/insert/update (even with Xceed's grid) quite easily, but does not save implicitly, ie. you have to manage it yourself.

If you prefer to batch up all your changes and save en mass at a special point (this is a common UI pattern) - then this might be a great solution for you.


One issue with with approach however, is that it only supports the whole table, ie. not based on queries. This might be fine if you are using the WPF's data filters, but it's not an optimal result with large amounts of data.


I'm planning to investigate these two aspects in the near future, and hopefully I'll find an elegant solution.

Friday, September 21, 2007

Linq with Xceed's WPF Grid - Part III - Hooking up the Delete

One of the under appreciated features of WPF is its Command support. This feature allows you to hook up events in a more abstract and loosely coupled way; with a great collection of common actions provided by default.

To hook up delete functionality from Xceed's data grid to a Linq data table, I took advantage of these Command gems and as you will see, they make things very easy to use.

The provided ApplicationCommands.Delete command, comes with a localized string ("Delete") and associated key bindings ("Del" Key).

Handling this command in the LinqGrid is as simple as calling the following in the initialization routines:

CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete,
OnDeleteCommand, OnCanDeleteCommand));


Implementing the OnCanDeleteCommand to return true, if any items are selected and the OnDeleteCommand to delete the selected items, and it's done! Well almost.

Automatically deleting the selected items (with the "Del" key) without any warning is usually not the best behaviour, so I implemented a RoutedEvent called PreviewDelete, which allows you to show a message box and/or cancel the delete.

Show me the XAML

Another great benefit of using Commands is the ability to bind other UI elements as the action co-coordinator for the command. i.e Buttons, Context Menus, etc.

Not only is this clean and easy, but it also has built in routines to control the IsEnabled state. ie. Button is disabled if you cannot currently delete.


<Button Command="Delete"
CommandTarget="{Binding ElementName=users}"
Content="{Binding Command.Text, RelativeSource={RelativeSource self}}" />


The "Command" is quite simply a reference to the ApplicationCommands.Delete command, while the "CommandTarget" is the name I gave to my LinqGrid.

The unfortunately long "Content" attribute is the Buttons label, bound to the Command itself. Remember how I said the provided commands have localized names... this is how you access them in XAML. Thankfully, this is also purely optional.

This is all you need to do. When the button is pressed, the Command is fired and our LinqGrid performs the delete, first checking with PreviewDelete for its permission.

Download the sample solution for the full code.

Linq with Xceed's WPF Grid - Part II (and a half)

An excellent suggestion was raised from my last post (thanks Marcus), which is to make the Linq DataContext a property on the LinqGrid.

This way the LinqRow can access it via it's parent, rather than via the singleton (which is bad practice in my book).

I've put all this code in a solution (includes sample database and all), so I'll let the code speak for itself.

Download Sample Solution

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.

Linq with Xceed's WPF Grid

Binding a read only Linq data table to Xceed's WPF data grid couldn't be easier, but things get a little trickier when you want insert/delete/update functionality.

The general recommendation (from Xceed's samples) is to implement a derived IBindingList collection class to provide the behaviour yourself.

Sounds like a lot of work to do something simple, especially if you want to bind to a lot of different objects - so here's a generic one I've prepared.

BTW: I haven't seen any articles on the web regarding best practice with this, so I'd love any comments if there is a better way.



class LinqList<T> : BindingList<T>
{
ITable table = null;

public LinqList(ITable table)
: base(table.OfType<T>().ToList())
{
this.table = table;
}

protected override object AddNewCore()
{
T item = (T)base.AddNewCore();

if (table != null)
table.Add(item);

return item;
}

public override void EndNew(int itemIndex)
{
base.EndNew(itemIndex);

if (itemIndex >= 0)
DataSource.Context.SubmitChanges();
}

protected override void RemoveItem(int index)
{
if (table != null)
table.Remove(this[index]);

base.RemoveItem(index);
}
}


My first code draft uses the BindingList as a base class (to avoid implementing everything myself) and takes a Linq data table as a constructor parameter.

Notice the OfType() call in the constructor, this provides basic support for inheritance with Linq, but more on that in a later post.

I use a singleton to store my Linq data context (DataSource.Context) to call SubmitChanges() after adding a new item.



static class DataSource
{
static DataSource()
{
DataSource.Context = new MyDataContext();
}

static public MyDataContext Context { get; set; }
}


Binding to Xceed

Simply bind an instance of this class to your data grid, and you're almost set!

See Part II for some changes that need to be made to the Xceed grid, to make this binding easier.