Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 159, comments - 0, trackbacks - 0

My Links

News

Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2015!

Pre-order VB 2015 Unleashed Pre-order my new book "Visual Basic 2015 Unleashed". Click for more info!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

Follow me on Twitter!

CodePlex download Download my open-source projects from CodePlex!

Article Categories

Archives

Post Categories

.NET Framework

Blogroll

Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

Using Telerik controls in LightSwitch for exporting, filtering, and sorting data - part 2

In the previous post we saw how to create a Silverlight user control that embeds some controls from the RadControl for Silverlight by Telerik, to be used in LightSwitch applications.

Basically we created the control, replaced the built-in DataGrid in LightSwitch, performed the data-binding and we have seen how this perfectly works.

We also prepared some buttons for printing and exporting features, which will be implemented in this post. But we'll do more: we'll demonstrate that the previously created custom control is also reusable. Let's start from this point, and add a new screen of type EditableGrid pointing to the Orders collection:

Once the Screen Designer is ready, we can simply follow the instruction described in the previous post to replace the default DataGrid with the custom control. If we now run the application, we can see how the new screen correctly uses the custom control:

The reason about reusability is that the RadGridView supports, in its ItemsSource property, the assignment of a generic property called Value, instead of receving the hard-coded name of the collection. This is with no doubt a tremendous benefit.

Now we can move to make buttons active. Just to be clear: there is a lot of code but I cannot explain it in details because it has been entirely taken from the samples page from Telerik, which also offers official documentation about controls. So, first some Imports directives:

Imports System.Collections
Imports System.Collections.Generic
Imports Telerik.Windows.Controls
Imports System.IO
Imports System.Linq
Imports Telerik.Windows.Data
Imports System.Windows
Imports Telerik.Windows.Documents.FormatProviders
Imports Telerik.Windows.Documents.Model
Imports Telerik.Windows.Documents.FormatProviders.Pdf
Imports System.Windows.Printing

The first button is responsible for exporting to Excel. Particularly we decide to take advantage of a built-in format for exporting to Csv:

    Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
        Dim extension As String = "csv"
        'Html and Word formats are also available
        Dim format As ExportFormat = ExportFormat.Csv
 
        Dim dialog As New SaveFileDialog()
        dialog.DefaultExt = extension
        dialog.Filter = String.Format("{1} files (*.{0})|*.{0}|All files (*.*)|*.*", extension, "Excel")
        dialog.FilterIndex = 1
 
        If dialog.ShowDialog() = True Then
            Using stream As IO.Stream = dialog.OpenFile()
                Dim exportOptions As New GridViewExportOptions()
                exportOptions.Format = format
                exportOptions.ShowColumnFooters = True
                exportOptions.ShowColumnHeaders = True
                exportOptions.ShowGroupFooters = True
 
                RadGridView1.Export(stream, exportOptions)
            End Using
        End If
 
    End Sub

The second button is responsible for printing, which is a fundamental feature and that can be implemented with a small effort:

    Private Sub PrintBitton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles PrintBitton.Click
        offsetY = 0
        totalHeight = 0
 
        grid = New RadGridView()
        grid.DataContext = RadGridView1.DataContext
        grid.ItemsSource = RadGridView1.ItemsSource
        grid.RowIndicatorVisibility = Visibility.Collapsed
        grid.ShowGroupPanel = False
        grid.CanUserFreezeColumns = False
        grid.IsFilteringAllowed = False
        grid.AutoExpandGroups = True
        grid.AutoGenerateColumns = False
 
        For Each column As GridViewDataColumn In RadGridView1.Columns.OfType(Of GridViewDataColumn)()
            Dim newColumn As New GridViewDataColumn()
            newColumn.DataMemberBinding = New System.Windows.Data.Binding(column.UniqueName)
            grid.Columns.Add(newColumn)
        Next
 
        For Each column As GridViewDataColumn In grid.Columns.OfType(Of GridViewDataColumn)()
            Dim currentColumn As GridViewDataColumn = column
 
            Dim originalColumn As GridViewDataColumn = (From c In RadGridView1.Columns.OfType(Of GridViewDataColumn)() _
             Where c.UniqueName = currentColumn.UniqueName _
             Select c).FirstOrDefault()
            If originalColumn IsNot Nothing Then
                column.Width = originalColumn.ActualWidth
                column.DisplayIndex = originalColumn.DisplayIndex
            End If
        Next
 
        StyleManager.SetTheme(grid, StyleManager.GetTheme(RadGridView1))
 
        grid.SortDescriptors.AddRange(RadGridView1.SortDescriptors)
        grid.GroupDescriptors.AddRange(RadGridView1.GroupDescriptors)
        grid.FilterDescriptors.AddRange(RadGridView1.FilterDescriptors)
 
        ScrollViewer.SetHorizontalScrollBarVisibility(grid, ScrollBarVisibility.Hidden)
        ScrollViewer.SetVerticalScrollBarVisibility(grid, ScrollBarVisibility.Hidden)
 
        Dim doc As New PrintDocument()
 
        canvas = New Canvas()
        canvas.Children.Add(grid)
 
        AddHandler doc.PrintPage, AddressOf Me.doc_PrintPage
        doc.Print("RadGridView print")
    End Sub
 
    Private Sub doc_PrintPage(sender As Object, e As PrintPageEventArgs)
        e.PageVisual = canvas
 
        If totalHeight = 0 Then
            totalHeight = grid.DesiredSize.Height
        End If
 
        canvas.SetTop(grid, -offsetY)
 
        offsetY += e.PrintableArea.Height
        e.HasMorePages = offsetY <= totalHeight
 
    End Sub

Now it is time for implementing exporting to PDF. This requires adding a reference to the Telerik.Windows.Documents and Telerik.Windows.Documents.FormatProviders.Pdf assemblies. Other export providers are available, but let's focus on PDF. Next, you need the following code:

   Private Sub ExportPDFButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles ExportPDFButton.Click
        Dim dialog As New SaveFileDialog()
        dialog.DefaultExt = "*.pdf"
        dialog.Filter = "Adobe PDF Document (*.pdf)|*.pdf"
 
        If dialog.ShowDialog() = True Then
            Dim document As RadDocument = CreateDocument(RadGridView1)
 
            document.LayoutMode = DocumentLayoutMode.Paged
 
            document.Measure(RadDocument.MAX_DOCUMENT_SIZE)
            document.Arrange(New RectangleF(PointF.Empty, document.DesiredSize))
 
            Dim provider As New PdfFormatProvider()
 
            Using output As Stream = dialog.OpenFile()
                provider.Export(document, output)
            End Using
        End If
 
    End Sub
 
    Private Function CreateDocument(grid As RadGridView) As RadDocument
        Dim columns As List(Of GridViewBoundColumnBase) = (From c In grid.Columns.OfType(Of GridViewBoundColumnBase)() _
         Order By c.DisplayIndex _
         Select c).ToList()
 
        Dim table As New Table()
 
        Dim document As New RadDocument()
        Dim section As New Section()
        section.Blocks.Add(table)
        document.Sections.Add(section)
 
        If grid.ShowColumnHeaders Then
            Dim headerRow As New TableRow()
 
            If grid.GroupDescriptors.Count() > 0 Then
                Dim indentCell As New TableCell()
                indentCell.PreferredWidth = New TableWidthUnit(grid.GroupDescriptors.Count() * 20)
                indentCell.Background = Colors.Blue
                headerRow.Cells.Add(indentCell)
            End If
 
            For i As Integer = 0 To columns.Count() - 1
                Dim cell As New TableCell()
                cell.Background = Colors.White
                AddCellValue(cell, columns(i).UniqueName)
                cell.PreferredWidth = New TableWidthUnit(CSng(columns(i).ActualWidth))
                headerRow.Cells.Add(cell)
            Next
 
            table.Rows.Add(headerRow)
        End If
 
        If grid.Items.Groups IsNot Nothing Then
            For i As Integer = 0 To grid.Items.Groups.Count() - 1
                AddGroupRow(table, TryCast(grid.Items.Groups(i), QueryableCollectionViewGroup), columns, grid)
            Next
        Else
            AddDataRows(table, grid.Items, columns, grid)
        End If
 
        Return document
    End Function
 
    Private Sub AddDataRows(table As Table, items As IList, columns As IList(Of GridViewBoundColumnBase), grid As RadGridView)
        For i As Integer = 0 To items.Count - 1
            Dim row As New TableRow()
 
            If grid.GroupDescriptors.Count() > 0 Then
                Dim indentCell As New TableCell()
                indentCell.PreferredWidth = New TableWidthUnit(grid.GroupDescriptors.Count() * 20)
                indentCell.Background = Colors.Blue
                row.Cells.Add(indentCell)
            End If
 
            For j As Integer = 0 To columns.Count() - 1
                Dim cell As New TableCell()
 
                Dim value As Object = columns(j).GetValueForItem(items(i))
 
                AddCellValue(cell, If(value IsNot Nothing, value.ToString(), String.Empty))
 
                cell.PreferredWidth = New TableWidthUnit(CSng(columns(j).ActualWidth))
                cell.Background = Colors.White
 
                row.Cells.Add(cell)
            Next
 
            table.Rows.Add(row)
        Next
    End Sub
 
    Private Sub AddGroupRow(table As Table, group As QueryableCollectionViewGroup, columns As IList(Of GridViewBoundColumnBase), grid As RadGridView)
        Dim row As New TableRow()
 
        Dim level As Integer = GetGroupLevel(group)
        If level > 0 Then
            Dim cell As New TableCell()
            cell.PreferredWidth = New TableWidthUnit(level * 20)
            cell.Background = Colors.Gray
            row.Cells.Add(cell)
        End If
 
        Dim aggregatesCell As New TableCell()
        aggregatesCell.Background = Colors.DarkGray
        aggregatesCell.ColumnSpan = columns.Count() + (If(grid.GroupDescriptors.Count() > 0, 1, 0)) - (If(level > 0, 1, 0))
 
        AddCellValue(aggregatesCell, If(group.Key IsNot Nothing, group.Key.ToString(), String.Empty))
 
        For Each result As AggregateResult In group.AggregateResults
            AddCellValue(aggregatesCell, If(result.FormattedValue IsNot Nothing, result.FormattedValue.ToString(), String.Empty))
        Next
 
        row.Cells.Add(aggregatesCell)
 
        table.Rows.Add(row)
 
        If group.HasSubgroups Then
            For i As Integer = 0 To group.Subgroups.Count() - 1
                AddGroupRow(table, TryCast(group.Subgroups(i), QueryableCollectionViewGroup), columns, grid)
            Next
        Else
            For i As Integer = 0 To group.ItemCount - 1
                AddDataRows(table, group.Items, columns, grid)
            Next
        End If
    End Sub
 
    Private Sub AddCellValue(cell As TableCell, value As String)
        Dim paragraph As New Paragraph()
        cell.Blocks.Add(paragraph)
 
        Dim span As New Span()
        span.Text = value
 
        paragraph.Inlines.Add(span)
    End Sub
 
    Private Function GetGroupLevel(group As IGroup) As Integer
        Dim level As Integer = 0
 
        Dim parent As IGroup = group.ParentGroup
 
        While parent IsNot Nothing
            level += 1
            parent = parent.ParentGroup
        End While
 
        Return level
    End Function

You can customize the Background properties and row/columns for different layouts. Now let's run the application and play with the buttons. For instance, exporting to CSV generates the following output:

This instead the result of exporting to PDF:

Also, with this technique we can export data even if the application is running inside your Web browser because the Telerik approach does not rely on COM automation.

Just to summarize

  • the suite from Telerik is a good product, but it just an example. You can use controls from other vendors, but also free and open source controls that support XAML data-binding
  • LightSwitch can be extended and allows embedding complex user controls
  • if something is not available in LightSwitch, can be added with a limited effort. A small amount of XAML code, a small amount of managed code.

Alessandro

Print | posted on giovedì 19 maggio 2011 15:19 | Filed Under [ Visual Basic Visual Studio LightSwitch ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 7 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET