Imports System.Reflection
Imports System.Collections
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Net
Imports System.Windows
Imports System.Windows.Data
Imports System.Windows.Controls
Imports System.Windows.Documents
Imports System.Windows.Input
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Shapes
Imports C1.Silverlight.FlexGrid

Namespace MainTestApplication
	Partial Public Class MainPage
		Inherits UserControl
		Private Const ROW_COUNT As Integer = 500

		Public Sub New()
			InitializeComponent()

			' create "Customers" data view and bind it to grids
			BindGroupingGrids() ' sync
			'Dispatcher.BeginInvoke(BindGroupingGrids); // async

			' create "Songs" data view and bind it to iTunes grid
			BindITunesGrid() ' sync
			'Dispatcher.BeginInvoke(BindITunesGrid);

			' populate unbound grid
			Dispatcher.BeginInvoke(AddressOf PopulateUnboundGrid)

			' populate financial grid
			Dispatcher.BeginInvoke(AddressOf PopulateFinancialGrid)

			' populate edit grid
			Dispatcher.BeginInvoke(AddressOf PopulateEditGrid)
		End Sub

		'-------------------------------------------------------------------------------
		#Region "** Main Grids"

		Private Sub BindGroupingGrids()
			' create PagedCollectionView used as a data source for the
			' FlexGrid and for the MS DataGrid
			Dim data = Customer.GetCustomerList(100)
			Dim view = New PagedCollectionView(data)
			Using view.DeferRefresh()
				view.GroupDescriptions.Clear()
				view.GroupDescriptions.Add(New PropertyGroupDescription("Country"))
				view.GroupDescriptions.Add(New PropertyGroupDescription("Active"))
				Dim gd = TryCast(view.GroupDescriptions(0), PropertyGroupDescription)
				gd.Converter = New CountryInitialConverter()
			End Using

			' bind grids to PagedCollectionView
			_flexGroup.ItemsSource = view
			_flexSelectionMode.ItemsSource = view
		End Sub

		' look for text in the grid
		Private Sub _txtFind_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
			' select row in the FlexGrid
			Dim col As Integer = _flexGroup.Columns("Name").Index
			Dim row As Integer = FindRow(_flexGroup, _txtFind.Text, _flexGroup.Selection.Row, col, True)
			If row > -1 Then
				_flexGroup.Select(row, col)
				_txtFind.Background = New SolidColorBrush(Colors.White)
			Else
				_txtFind.Background = New SolidColorBrush(Color.FromArgb(&Hff, &Hff, &Hee, &Hee))
			End If
		End Sub

		' look for a row that contains some text in a specific column
		Private Function FindRow(ByVal flex As C1FlexGrid, ByVal text As String, ByVal startRow As Integer, ByVal col As Integer, ByVal wrap As Boolean) As Integer
			Dim count As Integer = flex.Rows.Count
			For [off] As Integer = 0 To count
				' reached the bottom and not wrapping? quit now
				If (Not wrap) AndAlso startRow + [off] >= count Then
					Exit For
				End If

				' get text from row
				Dim row As Integer = (startRow + [off]) Mod count
				Dim content = flex(row, col)

				' found? return row index
				If content IsNot Nothing AndAlso content.ToString().IndexOf(text, StringComparison.OrdinalIgnoreCase) > -1 Then
					Return row
				End If
			Next [off]

			' not found...
			Return -1
		End Function

		' group by country or by initial
		Private Sub _chkGroupByInitial_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			If _flexGroup IsNot Nothing Then
				Dim view = TryCast(_flexGroup.ItemsSource, ICollectionView)
				Dim gd = TryCast(view.GroupDescriptions(0), PropertyGroupDescription)
				gd.Converter = If((CType(sender, CheckBox)).IsChecked.Value, New CountryInitialConverter(), Nothing)
			End If
		End Sub

		' turn merging on and off
		Private Sub _chkAllowMerging_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			Dim f = _flexGroup
			If f IsNot Nothing Then
				Dim allowMerging = (CType(sender, CheckBox)).IsChecked.Value
                f.AllowMerging = If(allowMerging, C1.Silverlight.FlexGrid.AllowMerging.Cells, C1.Silverlight.FlexGrid.AllowMerging.None)
				f.Columns("Country").AllowMerging = allowMerging
				f.Columns("First").AllowMerging = allowMerging
				f.Columns("Last").AllowMerging = allowMerging
			End If
		End Sub

		' show groups
		Private Sub _chkShowGroups_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexGroup.ShowGroups = (CType(sender, CheckBox)).IsChecked.Value
		End Sub

		' keep current item visible 
		' (e.g. is the user selects an object in the MS grid, the flex will
		' automatically scroll to keep the object in view)
		Private Sub _chkKeepCurrentVisible_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexGroup.KeepCurrentVisible = (CType(sender, CheckBox)).IsChecked.Value
		End Sub

		' freeze/unfreeze panes
		Private Sub _chkFreezePanes_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			If _chkFreezePanes.IsChecked.Value Then
				_flexGroup.Rows.Frozen = Math.Min(_flexGroup.Selection.Row, 5)
				_flexGroup.Columns.Frozen = Math.Min(_flexGroup.Selection.Column, 3)
			Else
				_flexGroup.Rows.Frozen = 0
				_flexGroup.Columns.Frozen = 0
			End If
		End Sub

		' change selection mode
		Private Sub _cmbSelectionMode_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
			If _cmbSelectionMode IsNot Nothing Then
				_flexSelectionMode.SelectionMode = CType(_cmbSelectionMode.SelectedIndex, C1.Silverlight.FlexGrid.SelectionMode)
			End If
		End Sub

		' turn merging on and off
		Private Sub _chkAllowMergingSelMode_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			Dim f = _flexSelectionMode
			If f IsNot Nothing Then
				Dim allowMerging = (CType(sender, CheckBox)).IsChecked.Value
                f.AllowMerging = If(allowMerging, C1.Silverlight.FlexGrid.AllowMerging.Cells, C1.Silverlight.FlexGrid.AllowMerging.None)
				f.Columns("Country").AllowMerging = allowMerging
				f.Columns("First").AllowMerging = allowMerging
				f.Columns("Last").AllowMerging = allowMerging
			End If
		End Sub

		#End Region

		'-------------------------------------------------------------------------------
		#Region "** iTunes grid"

		Private Sub BindITunesGrid()
			Dim songs = MediaLibrary.Load()
			Dim view = New PagedCollectionView(songs)
			Using view.DeferRefresh()
				view.GroupDescriptions.Clear()
				view.GroupDescriptions.Add(New PropertyGroupDescription("Artist"))
				view.GroupDescriptions.Add(New PropertyGroupDescription("Album"))
			End Using
			Dim fg = _flexiTunes
			fg.CellFactory = New MusicCellFactory()
			fg.MergeManager = Nothing ' << review this, should not merge cells with content
			fg.Columns("Duration").ValueConverter = New SongDurationConverter()
			fg.Columns("Size").ValueConverter = New SongSizeConverter()
			fg.ItemsSource = view

			' configure search box
			_srchTunes.View = view
			For Each name As String In "Artist|Album|Name".Split("|"c)
				_srchTunes.FilterProperties.Add(GetType(Song).GetProperty(name))
			Next name

			' customize group header content (used by the default CellFactory)
			fg.GroupHeaderConverter = New GroupHeaderConverter()

			' show media library summary (using Linq)
			AddHandler view.CollectionChanged, AddressOf songs_CollectionChanged
			UpdateSongStatus()
		End Sub
		Private Sub songs_CollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
			UpdateSongStatus()
		End Sub

		' update song status
		Private Sub UpdateSongStatus()
			Dim view = TryCast(_flexiTunes.ItemsSource, ICollectionView)
			Dim songs = view.OfType(Of Song)()
            'INSTANT VB TODO TASK: Assignments within expressions are not supported in VB
            'ORIGINAL LINE: _txtSongs.Text = string.Format("{0:n0} Artists; {1:n0} Albums; {2:n0} Songs; {3:n0} MB of storage; {4:n2} days of music.", (from s in songs select s.Artist).Distinct().Count(), (from s in songs select s.Album).Distinct().Count(), (from s in songs select s.Name).Count(), (double)(from s in songs select s.Size / 1024.0 / 1024.0).Sum(), (double)(from s in songs select s.Duration / 1000.0 / 3600.0 / 24.0).Sum());
			_txtSongs.Text = String.Format("{0:n0} Artists; {1:n0} Albums; {2:n0} Songs; {3:n0} MB of storage; {4:n2} days of music.", ( _
			    From s In songs _
			    Select s.Artist).Distinct().Count(), ( _
			    From s In songs _
			    Select s.Album).Distinct().Count(), ( _
			    From s In songs _
			    Select s.Name).Count(), CDbl(( _
			        From s In songs _
			        Select s.Size / 1024.0 / 1024.0).Sum()), CDbl(( _
			        From s In songs _
			        Select s.Duration / 1000.0 / 3600.0 / 24.0).Sum()))
		End Sub

		' turn custom cells on and off
		Private Sub _chkOwnerDraw_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexiTunes.CellFactory = If(_chkOwnerDraw.IsChecked.Value, New MusicCellFactory(), Nothing)
		End Sub

		' collapse/expand groups
		Private Sub _btnShowArtists_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexiTunes.CollapseGroupsToLevel(0)
		End Sub
		Private Sub _btnShowAlbums_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexiTunes.CollapseGroupsToLevel(1)
		End Sub
		Private Sub _btnShowSongs_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexiTunes.CollapseGroupsToLevel(2)
		End Sub

		' converter for artist/album groups
		Public Class GroupHeaderConverter
			Implements IValueConverter
            Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
                ' return group name only (no counts)
                Dim group = TryCast(value, CollectionViewGroup)
                If group IsNot Nothing AndAlso targetType Is GetType(String) Then
                    Return group.Name
                End If

                ' default
                Return value
            End Function
            Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
                Throw New NotImplementedException()
            End Function
		End Class

		' converter for song durations (stored in milliseconds)
		Private Class SongDurationConverter
			Implements IValueConverter
            Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
                Dim ts = TimeSpan.FromMilliseconds(CLng(Fix(value)))
                Return If(ts.Hours = 0, String.Format("{0:00}:{1:00}", ts.Minutes, ts.Seconds), String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds))
            End Function
            Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
                Throw New NotImplementedException()
            End Function
		End Class

		' converter for song sizes (returns x.xx MB)
		Private Class SongSizeConverter
			Implements IValueConverter
            Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
                Return String.Format("{0:n2} MB", CLng(Fix(value)) / 1024.0 / 1024.0)
            End Function
            Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
                Throw New NotImplementedException()
            End Function
		End Class

		#End Region

		'-------------------------------------------------------------------------------
		#Region "** Unbound grid"

		' fill unbound grid with data
		Private Sub PopulateUnboundGrid()
			' allow merging
			Dim fg = _flexUnbound
			fg.AllowMerging = AllowMerging.All

			' add rows/columns to the unbound grid
			For i As Integer = 0 To 11 ' three years, four quarters per year
				fg.Columns.Add(New Column())
			Next i
			For i As Integer = 0 To 499
				fg.Rows.Add(New Row())
			Next i

			' populate the unbound grid with some stuff
			For r As Integer = 0 To fg.Rows.Count - 1
				For c As Integer = 0 To fg.Columns.Count - 1
					fg(r, c) = String.Format("cell [{0},{1}]", r, c)
				Next c
			Next r

			' add some group rows above the data
			For offset As Integer = 0 To fg.Rows.Count - 10 - 1 Step 10
				For i As Integer = 0 To 2
					fg.Rows.Insert(offset + i, New GroupRow() With {.Level = i})
					fg(offset + i, 0) = String.Format("level {0}", i)
				Next i
			Next offset

			' set unbound column headers
			Dim ch = fg.ColumnHeaders
			ch.Rows.Add(New Row()) ' one header row for years, one for quarters
			For c As Integer = 0 To ch.Columns.Count - 1
				ch(0, c) = 2009 + c \ 4 ' year
				ch(1, c) = String.Format("Q {0}", c Mod 4 + 1) ' quarter
			Next c

			' allow merging the first fixed row
			ch.Rows(0).AllowMerging = True

			' set unbound row headers
			Dim rh = fg.RowHeaders
			rh.Columns.Add(New Column())
			For c As Integer = 0 To rh.Columns.Count - 1
				rh.Columns(c).Width = New GridLength(60)
				For r As Integer = 0 To rh.Rows.Count - 1
					rh(r, c) = String.Format("hdr {0},{1}",If(c = 0, r \ 2, r), c)
				Next r
			Next c

			' allow merging the first fixed column
			rh.Columns(0).AllowMerging = True

			' listen to column resized event
			AddHandler _flexUnbound.ResizedColumn, AddressOf _flexUnbound_ResizedColumn
		End Sub

		' make all unbound grid columns the same width after they are resized
		Private Sub _flexUnbound_ResizedColumn(ByVal sender As Object, ByVal e As CellRangeEventArgs)
			Dim col = _flexUnbound.Columns(e.Column)
			If col.ActualWidth > 0 Then
				_flexUnbound.Columns.DefaultSize = col.ActualWidth
			End If
			col.Width = New GridLength(0, GridUnitType.Auto)
		End Sub

		#End Region

		'-------------------------------------------------------------------------------
		#Region "** SelectionMode grid"

		Private Sub _flexSelectionMode_SelectionChanged(ByVal sender As Object, ByVal e As CellRangeEventArgs)
			Dim fg = _flexSelectionMode
			Dim customers = fg.Rows.GetDataItems(fg.Selection).OfType(Of Customer)()
'INSTANT VB TODO TASK: Assignments within expressions are not supported in VB
'ORIGINAL LINE: _lblSelState.Text = string.Format("{0} items selected, {1} active, total weight: {2:n2}", customers.Count(), (from c in customers where c.Active select c).Count(), (from c in customers select c.Weight).Sum());
			_lblSelState.Text = String.Format("{0} items selected, {1} active, total weight: {2:n2}", customers.Count(), ( _
			    From c In customers _
			    Where c.Active _
			    Select c).Count(), ( _
			    From c In customers _
			    Select c.Weight).Sum())
		End Sub
		Private Shared Function GetClipString(ByVal fg As C1FlexGrid, ByVal sel As CellRange) As String
			Dim sb = New System.Text.StringBuilder()
			For r As Integer = sel.TopRow To sel.BottomRow
				For c As Integer = sel.LeftColumn To sel.RightColumn
					sb.AppendFormat("{0}" & vbTab, fg(r, c).ToString())
				Next c
				sb.AppendLine()
			Next r
			Return sb.ToString()
		End Function
		#End Region

		'-------------------------------------------------------------------------------
		#Region "** Financial grid"

		Private _financialData As FinancialDataList

		Private Sub PopulateFinancialGrid()
			_financialData = FinancialData.GetFinancialData()
			Dim view = New PagedCollectionView(_financialData)
			_flexFinancial.ItemsSource = view
			_flexFinancial.Columns.Frozen = 1
			_flexFinancial.Columns(0).AllowDragging = False

			' configure search box
			_srchCompanies.View = view
			Dim props = _srchCompanies.FilterProperties
			props.Add(GetType(FinancialData).GetProperty("Name"))
			props.Add(GetType(FinancialData).GetProperty("Symbol"))

			' show company info
			UpdateCompanyStatus()
			AddHandler view.CollectionChanged, AddressOf financial_CollectionChanged
		End Sub

		Private Sub financial_CollectionChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
			UpdateCompanyStatus()
		End Sub

		' update song status
		Private Sub UpdateCompanyStatus()
			Dim view = TryCast(_flexFinancial.ItemsSource, ICollectionView)
			Dim companies = view.OfType(Of FinancialData)()
'INSTANT VB TODO TASK: Assignments within expressions are not supported in VB
'ORIGINAL LINE: _txtCompanies.Text = string.Format("{0:n0} companies selected.", (from c in companies select c.Symbol).Distinct().Count());
			_txtCompanies.Text = String.Format("{0:n0} companies selected.", ( _
			    From c In companies _
			    Select c.Symbol).Distinct().Count())
		End Sub

		' control update frequency
		Private Sub _chkAutoUpdate_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_financialData.AutoUpdate = (CType(sender, CheckBox)).IsChecked.Value
		End Sub
		Private Sub _cmbUpdateInterval_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
			If _financialData IsNot Nothing Then
				Dim cmb = TryCast(sender, ComboBox)
				Dim val = TryCast((CType(cmb.SelectedItem, ComboBoxItem)).Content, String)
				val = val.Split(" "c)(0).Trim()
				_financialData.UpdateInterval = Integer.Parse(val)
			End If
		End Sub
		Private Sub _cmbBatchSize_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
			If _financialData IsNot Nothing Then
				Dim cmb = TryCast(sender, ComboBox)
				Dim val = TryCast((CType(cmb.SelectedItem, ComboBoxItem)).Content, String)
				val = val.Split(" "c)(0).Trim()
				_financialData.BatchSize = Integer.Parse(val)
			End If
		End Sub
		Private Sub _chkOwnerDrawFinancial_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
			_flexFinancial.CellFactory = If((CType(sender, CheckBox)).IsChecked.Value, New FinancialCellFactory(), Nothing)
		End Sub

		#End Region

		'-------------------------------------------------------------------------------
		#Region "** Edit grid"

		Private Sub PopulateEditGrid()
			' create the data
			Dim data = Customer.GetCustomerList(100)
			Dim view = New PagedCollectionView(data)
			_flexEdit.ItemsSource = view

			' hide read-only "Country" column 
			Dim col = _flexEdit.Columns("Country")
			col.Visible = False

			' map countryID column so it shows country names instead of their IDs
			Dim dct As New Dictionary(Of Integer, String)()
			For Each country In Customer.GetCountries()
				dct(dct.Count) = country
			Next country
			col = _flexEdit.Columns("CountryID")
			col.ValueConverter = New ColumnValueConverter(dct)
			col.HorizontalAlignment = HorizontalAlignment.Left
			col.Width = New GridLength(120)

			' provide auto-complete lists for first and last name columns
			col = _flexEdit.Columns("First")
			col.ValueConverter = New ColumnValueConverter(Customer.GetFirstNames(), False)
			col = _flexEdit.Columns("Last")
			col.ValueConverter = New ColumnValueConverter(Customer.GetLastNames(), False)

			' make read-only columns look disabled
			Dim readOnlyBrush = New SolidColorBrush(Color.FromArgb(&He0, &He0, &He0, &He0))
			For Each c In _flexEdit.Columns
				If c.PropertyInfo IsNot Nothing AndAlso (Not c.PropertyInfo.CanWrite) Then
					c.Background = readOnlyBrush
				End If
			Next c
		End Sub
		#End Region
	End Class

	' converter used to group countries by their first initial
	Friend Class CountryInitialConverter
		Implements IValueConverter
        Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
            Return (CStr(value))(0).ToString().ToUpper()
        End Function
        Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
            Throw New NotImplementedException()
        End Function
	End Class
End Namespace
