Imports System.Data
Imports System.Drawing
Imports System.ComponentModel
Imports Mercury.Mobile.Forms.Primitives

''' <summary>
''' Represents a listbox whose elements can be highlighted based on the value of a property.
''' </summary>
''' <remarks></remarks>
Public Class MarkableListBox
    Inherits OwnerDrawnList

    Private Const DRAW_OFFSET As Integer = 2
    'Private _borderColor As Color

    ' Row flags
    Private _flagPropertyName As String
    Private _canFlagRows As Boolean
    Private _flagHighlightColor As Color

    ' Backbuffer for double buffering
    Private backBuffer As Bitmap

    Public Sub New()
        '' Determine what the item height should be
        '' by adding 10% padding after measuring
        '' the letter A with the selected font.
        'Dim g As Graphics = Me.CreateGraphics()
        'Me.ItemHeight = Fix(g.MeasureString("A", Me.Font).Height + 1.1)
        'g.Dispose()

        ' Set initial colors
        Me.BackColor = (System.Drawing.SystemColors.Window)
        Me.ForeColor = (System.Drawing.SystemColors.WindowText)

        ' Set border
        Me.BorderColor = Color.FromArgb(0, 0, 0)
        Me.ShowBorder = True

        ' Set default highlight color
        Me._flagHighlightColor = Color.Yellow

        ' Testing
        Me._canFlagRows = False
        Me._flagPropertyName = String.Empty
    End Sub

#Region "Properties"

    ''' <summary>
    ''' Gets/sets the value determining whether the MobileListBox
    ''' will highlight a row with a True value on the specified
    ''' flag column.
    ''' </summary>
    ''' <value>Boolean.</value>
    ''' <returns>True if set to highlight flagged rows; otherwise, false.</returns>
    ''' <remarks></remarks>
    Public Property CanHighlightFlagRows() As Boolean
        Get
            Return Me._canFlagRows
        End Get
        Set(ByVal value As Boolean)
            Me._canFlagRows = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the name of the row flag column.
    ''' </summary>
    ''' <value>String.</value>
    ''' <returns>Flag column name.</returns>
    ''' <remarks></remarks>
    Public Property FlagColumnName() As String
        Get
            Return Me._flagPropertyName
        End Get
        Set(ByVal value As String)
            Me._flagPropertyName = value
        End Set
    End Property

    ''' <summary>
    ''' Gets/sets the color used for highlighting flagged rows.
    ''' </summary>
    ''' <value>System.Drawing.Color.</value>
    ''' <returns>System.Drawing.Color for highlighting flagged rows.</returns>
    ''' <remarks></remarks>
    Public Property FlagRowHighlightColor() As Color
        Get
            Return Me._flagHighlightColor
        End Get
        Set(ByVal value As Color)
            Me._flagHighlightColor = value
        End Set
    End Property

    ''' <summary>
    ''' Returns the text representation of the specified item.
    ''' </summary>
    ''' <param name="item">The item from which to get the contents to display.</param>
    ''' <value>String.</value>
    ''' <returns>If the DisplayMember property is not specified, the value returned by GetItemText 
    ''' is the value of the item's ToString method. Otherwise, the method returns the string value 
    ''' of the member specified in the DisplayMember property for the object specified in the item 
    ''' parameter.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property GetItemText(ByVal item As Object) As String
        Get
            ' If expected type is a MobileDataSlice
            If TypeOf (item) Is Row AndAlso Not (String.IsNullOrEmpty(Me.DisplayMember)) Then
                Return CType(item, Row).Item(Me.DisplayMember).ToString()
            Else    ' Return object.tostring
                Return item.ToString()
            End If
        End Get
    End Property

    ''' <summary>
    ''' Gets the currently selected item in the MobileListBox.
    ''' </summary>
    ''' <value></value>
    ''' <returns>An object that represents the current selection in the control.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property SelectedItem() As Object
        Get
            ' If greater than -1
            If Me.SelectedIndex > -1 Then
                Return Me.Items(Me.SelectedIndex)
            Else    ' Return null
                Return Nothing
            End If
        End Get
    End Property

#End Region

#Region "Events"

    Protected Overrides Sub OnDrawItem(ByVal sender As Object, ByVal e As DrawItemEventArgs)
        Dim textBrush As Brush
        Dim renderFont As Font

        ' If brushes not cached
        If Me.cacheTextHighlightBrush Is Nothing Then
            Me.cacheTextHighlightBrush = New SolidBrush(System.Drawing.SystemColors.Highlight)
        End If
        If Me.cacheFlaggedRowHighlightBrush Is Nothing Then
            Me.cacheFlaggedRowHighlightBrush = New SolidBrush(Me._flagHighlightColor)
        End If
        If Me.cacheTextBrush Is Nothing Then
            Me.cacheTextBrush = New SolidBrush(Me.ForeColor)
        End If

        ' If event args and index within correct range
        If e IsNot Nothing AndAlso e.Index > -1 AndAlso e.Index < Me.Items.Count Then
            ' Get item bounds
            Dim rectItemBounds As Rectangle = e.Bounds
            rectItemBounds.X += DRAW_OFFSET
            rectItemBounds.Y += 1
            Dim backgroundBounds As Rectangle = e.GetBackgroundDrawArea
            backgroundBounds.Width -= DRAW_OFFSET
            backgroundBounds.Y += 1

            ' Get font
            renderFont = e.Font

            ' If highlight flag
            If IsFlaggedItem(e) Then
                ' Draw background color
                e.DrawBackground(Me._flagHighlightColor, backgroundBounds)
                ' Reverse current bold settings
                If renderFont.Style = FontStyle.Bold Then
                    renderFont = New Font(renderFont.Name, renderFont.Size, FontStyle.Regular)
                Else
                    renderFont = New Font(renderFont.Name, renderFont.Size, FontStyle.Bold)
                End If
            End If

            ' Check if item is selected
            If e.State = DrawItemState.Selected Then
                ' Highlighted
                e.DrawBackground(SystemColors.Highlight, backgroundBounds)
                textBrush = New SolidBrush(System.Drawing.SystemColors.HighlightText)
            Else    ' Not highlighted
                textBrush = Me.cacheTextBrush
            End If

            ' Get item
            Dim item As String = Me.Items(e.Index).Item(Me.DisplayMember).ToString()
            ' Draw text
            e.Graphics.DrawString(item, renderFont, textBrush, rectItemBounds)
            ' Draw base
            MyBase.OnDrawItem(sender, e)
        End If
    End Sub

#End Region

#Region "Drawing Methods"

    Private cacheTextBrush As Brush
    Private cacheTextHighlightBrush As Brush
    Private cacheFlaggedRowHighlightBrush As Brush

    Private Function IsFlaggedItem(ByVal e As DrawItemEventArgs) As Boolean
        ' If can flag rows
        If Me._canFlagRows Then
            ' If valid flag property
            If IsValidFlagProperty() Then
                Dim slice As Row = Me.Items(e.Index) 'Me.Items.GetSlice(e.Index)
                Try
                    Return Boolean.Parse(slice.Item(Me._flagPropertyName).ToString())
                Catch ex As ArgumentNullException
                    Return False
                Catch ex As FormatException
                    Return False
                End Try
            End If
        End If
    End Function

    Private Function IsValidFlagProperty() As Boolean
        ' Validate display member
        For Each propertySlice As Column In Me.ObjectTable.Columns
            If propertySlice.Name = Me.DisplayMember Then
                Return True
            End If
        Next
    End Function

#End Region

#Region "Methods"

    ''' <summary>
    ''' Gets the height of the items in the list.
    ''' </summary>
    ''' <returns>Integer item height.</returns>
    ''' <remarks></remarks>
    Public Function GetItemHeight() As Integer
        Return MyBase.ItemHeight
    End Function

    ''' <summary>
    ''' Sets the height of the items in the list.
    ''' </summary>
    ''' <param name="itemHeight">Integer height value for items in the list.</param>
    ''' <remarks></remarks>
    Public Sub SetItemHeight(ByVal itemHeight As Integer)
        MyBase.ItemHeight = itemHeight
    End Sub

    ''' <summary>
    ''' Gets the number of viewable items based on the client height
    ''' and the item height.
    ''' </summary>
    ''' <returns>Number of viewable items for the control.</returns>
    ''' <remarks></remarks>
    Public Function GetViewableItemCount() As Integer
        Return Fix(Me.ClientSize.Height / Me.ItemHeight)
    End Function

#End Region

End Class
