Live views is a powerful feature, so let’s take a little more time to see what live views can do. Live views are designed to make data binding more powerful, so powerful, in fact, that you can develop virtually entire applications with just LINQ (in its LiveLinq form) and data binding.
Live views, called LiveLinq, a part of ComponentOne Studio for Entity Framework (SEF), is a client-side, in-memory feature that is applicable to Entity Framework and RIA Services data, as we saw above, but to any observable collections in memory, including XML (LINQ to XML object model) and ADO.NET DataSets.
So, for example, you can use live views over RIA Services data and some other data (for example, XML retrieved from some web service) to integrate that data and to provide easy full-featured data binding to the integrated data. This is a powerful tool for building applications that get data from various sources. For now we’re going to concentrate on how we can use it with the Entity Framework, but if you’d like to explore LiveLinq in greater depth, see the ComponentOne LiveLinq documentation.
In fact, we already saw an example of such customization in Customizing View. But there we only changed the properties (fields) of the view and only applied one LINQ operator, Select. Let’s apply some more LINQ operators to transform the view. What we do here will be similar to what we did in Customizing View, but instead of using C1DataSource, we’ll be doing everything in code.
To use live views, follow these steps:
1. Add a new form using the project created to demonstrate Working with Data Sources in Code, and add a data grid, dataGridView1, to the form.
2. In the form’s Load event, add the following code:
Private _scope As RiaClientScope = App.ClientCache.CreateScope()
•C#
privateRiaClientScope _scope = App.ClientCache.CreateScope();
Here we are following the pattern recommended in Working with Data Sources in Code and creating a field in the user control class.
3. Getting Products data from the scope, we create a live view and bind the grid to that view in the user control’s constructor:
_viewProducts =
(From p In _scope.GetItems(Of Product)("GetProducts")
Where Not p.Discontinued And p.UnitPrice >= 30
Order By p.UnitPrice
Select New With
{
p.ProductID,
p.ProductName,
p.CategoryID,
p.Category.CategoryName,
p.SupplierID,
.Supplier = p.Supplier.CompanyName,
p.UnitPrice,
p.QuantityPerUnit,
p.UnitsInStock,
p.UnitsOnOrder
}).AsDynamic()
•C#
_viewProducts =
(from p in _scope.GetItems<Web.Product>("GetProducts")
where !p.Discontinued && p.UnitPrice >= 30
orderby p.UnitPrice
select new
{
ProductID = p.ProductID,
ProductName = p.ProductName,
CategoryID = p.CategoryID,
CategoryName = p.Category.CategoryName,
SupplierID = p.SupplierID,
UnitPrice = p.UnitPrice,
QuantityPerUnit = p.QuantityPerUnit,
UnitsInStock = p.UnitsInStock,
UnitsOnOrder = p.UnitsOnOrder
}).AsDynamic();
dataGrid1.ItemsSource = _viewProducts;
In this example, we applied several LiveLinq operators: Where, OrderBy, and Select. We defined our view as containing products that aren’t discontinued and have a unit price of at least 30, and we sorted our view by unit price.
We chose to store the view in a private field _viewProducts here:
Private _viewProducts As View(Of Object)
•C#
private View<dynamic> _viewProducts;
That is only because we will need it later. If we did not, we could use a local variable for the view.
4. Now run the project. You see that the grid shows the filtered set of products, "expensive" products that aren’t discontinued, in the order that we specified with the columns that we specified. Note also that all columns are modifiable, and you can even add and delete rows in the grid. Also note that you can sort the grid at run time by clicking column headers.
But live views offer even more great features. Standard LINQ to Objects produces snapshots of the data that cannot reflect changes in the source data, except some simple property changes, and even then under the strict proviso that you don’t utilize a custom Select in your LINQ statement. Live Views, on the other hand, provide dynamic ‘live’ views that automatically reflect changes in their source data. As such, they simplify application development because you can, in most cases, rely on data binding to automate ‘live’ changes in views without the need to synchronize the changes in different parts of the application with code.
To see another example of how live views automatically synchronize themselves with changes in underlying data, follow these steps:
1. Add a live view member of the user control class:
Private _seafoodProductsView As ClientView(Of Product)
•C#
private ClientView<Web.Product> _seafoodProductsView;
2. Add the following code to the form’s constructor:
_seafoodProductsView = _scope.GetItems(Of Product)("GetProducts").AsFiltered(Function(p) p.CategoryID.Value = 8)
•C#
_seafoodProductsView = _scope.GetItems<Web.Product>("GetProducts")
.AsFiltered(p => p.CategoryID == 8);
3. Add two buttons, named btnRaise and btnCut, to the form and add the following handlers to the form’s code :
Private Sub raiseSeafood_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
For Each p In _seafoodProductsView
p.UnitPrice *= 1.2
Next
End Sub
Private Sub cutSeafood_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
For Each p In _seafoodProductsView
p.UnitPrice /= 1.2
Next
End Sub
•C#
private void raiseSeafood(object sender, RoutedEventArgs e)
{
foreach (var p in _seafoodProductsView)
p.UnitPrice *= 1.2m;
}
private void cutSeafood(object sender, RoutedEventArgs e)
{
foreach (var p in _seafoodProductsView)
p.UnitPrice /= 1.2m;
}
4. Save, build and run the application. As you press the buttons, notice how seafood products appear in the grid because their unit price is now either greater than or equal to 30 or how they disappear when their unit price falls below 30. All of this happens automatically. You did not have to write any special code to refresh or synchronize the grid
5. To see how live views can make almost any GUI-related code easier to write (and less error-prone), let’s add a text block to the form that shows the current row count. Without SEF, this would usually be done in a method counting the rows, and that method would have to be called in every place in the code where that count can change. In this example, there would be three such places: on initial load, and in the two methods called when the buttons are pressed, raiseSeafood and cutSeafood. So it is not very easy to synchronize display with changing data even in this simple example, not to speak of a real application. Live views make all this synchronization code unnecessary. We already saw how it works for a view containing filtering, ordering, and projection (Where, OrderBy, and Select). It works as well for views containing aggregation operations, such as Count. A little difference here is that regular LINQ Count returns a single number, to which you can’t bind a control, so SEF provides a special operation LiveCount that returns a live view instead of a single number, so you can use it in data binding. We can create this binding with a single line of code:
textBlockCount.SetBinding(TextBlock.TextProperty, New Binding("Value") With {.Source = _viewProducts.LiveCount()})
•C#
textBlockCount.SetBinding(TextBlock.TextProperty,
newBinding("Value") { Source = _viewProducts.LiveCount() });