Imports System.Windows.Forms
Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Columns
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Grid

Public Class frmLookupGrid
    Public Property MultiSelect As Boolean
    Public Property AddNewValues As Boolean
    Public Property PreventDuplicates As Boolean
    Public Property DataSource As DataTable
    Public Property SelectedValues As List(Of String)
    Public Property NewValues As New HashSet(Of String)

    Public Const COLUMN_SELECTED = "SELECTED"
    Public Const COLUMN_VALUE = "VALUE"
    Public Const TABLE_TEMP = "TEMP"

    Private _DataColumn As Integer
    Private _DataSourceTemp As DataTable
    Private _View As GridView
    Private _Grid As GridControl
    Private _R As Resources.ResourceManager = My.Resources.Strings.ResourceManager

    Private Sub frmLookupGrid_Load(sender As Object, e As EventArgs) Handles Me.Load
        _View = viewLookup
        _Grid = gridLookup

        ' Original Datasource soll nicht verändert werden
        If DataSource Is Nothing Then
            _DataSourceTemp = New DataTable(TABLE_TEMP)
            _DataSourceTemp.Columns.Add(New DataColumn(COLUMN_VALUE))
        Else
            _DataSourceTemp = DataSource.Copy()
        End If

        If MultiSelect Then
            If Not _DataSourceTemp.Columns.Contains(COLUMN_SELECTED) Then
                Dim oSelectedColumn = New DataColumn() With {
                    .ColumnName = COLUMN_SELECTED,
                    .DataType = GetType(Boolean),
                    .DefaultValue = False
                }
                _DataSourceTemp.Columns.Add(oSelectedColumn)
                oSelectedColumn.SetOrdinal(0)
            End If
        End If

        ' Datasource setzen
        _Grid.DataSource = _DataSourceTemp

        ' Anzeige Eigeschaften setzen
        _View.OptionsFind.Condition = DevExpress.Data.Filtering.FilterCondition.Contains
        _View.OptionsFind.AlwaysVisible = True
        _View.OptionsSelection.MultiSelect = False

        If MultiSelect Then
            ' Selected Spalte anpassen
            Dim oCheckboxColumn = _View.Columns.Item(0)
            oCheckboxColumn.Caption = " "
            oCheckboxColumn.MaxWidth = 30
            oCheckboxColumn.MinWidth = 30

            Text = _R.GetString("PopupForm_Text")
            _DataColumn = 1
        Else
            Text = _R.GetString("PopupForm_TextMultiLine")
            _DataColumn = 0
        End If

        If AddNewValues Then
            _View.OptionsBehavior.AllowAddRows = DevExpress.Utils.DefaultBoolean.True
            _View.OptionsView.NewItemRowPosition = NewItemRowPosition.Top
        Else
            _View.OptionsBehavior.AllowAddRows = DevExpress.Utils.DefaultBoolean.False
            _View.OptionsView.NewItemRowPosition = NewItemRowPosition.None
        End If

        If SelectedValues Is Nothing Then
            SelectedValues = New List(Of String)
        Else
            SelectedValues = SelectedValues.Where(Function(v) Not (IsDBNull(v) OrElse String.IsNullOrWhiteSpace(v))).ToList()
        End If

        ' Bereits ausgewählte Werte im grid auswählen
        SyncItemsWithView(_View)

        ' Focus auf Find panel setzen
        _View.ShowFindPanel()

        ' Spaltenbreite anpassen
        _View.BestFitColumns()

        ' Datenspalte zusätzlich in der Breite anpassen
        Dim oDataColumn As GridColumn = _View.Columns.Item(_DataColumn)
        oDataColumn.BestFit()
    End Sub

    Private Sub SaveSelectedValues()
        ' Filter vor dem Auslesen entfernen, damit alle Werte erfasst werden
        _View.FindFilterText = String.Empty

        If MultiSelect Then
            Dim oValues As New List(Of String)

            For oIndex = 0 To viewLookup.DataRowCount - 1
                Dim oRow As DataRow = _View.GetDataRow(oIndex)
                Dim oSelected As Boolean = oRow.Item(0)
                Dim oValue As Object = GetValueFromRow(oRow)

                If oSelected Then
                    oValues.Add(oValue)
                End If
            Next

            ' Doppelte Werte entfernen, wenn konfiguriert
            If PreventDuplicates Then
                oValues = oValues.Distinct().ToList()
            End If

            SelectedValues = oValues
        Else
            Dim oRowHandle As Integer = _View.GetSelectedRows().ToList().FirstOrDefault()
            Dim oRow As DataRow = _View.GetDataRow(oRowHandle)
            Dim oValues As New List(Of String)

            If oRow IsNot Nothing Then
                Dim oValue = GetValueFromRow(oRow)
                oValues.Add(oValue)
            End If

            SelectedValues = oValues
        End If
    End Sub

    Private Function GetValueFromRow(pRow As DataRow) As String
        If MultiSelect Then
            Return pRow.Item(1)
        Else
            Return pRow.Item(0)
        End If
    End Function

    Private Sub SyncItemsWithView(view As GridView)
        ' Wenn Vorbelegungen existieren, werden diese angehakt
        If SelectedValues.Count > 0 Then
            For i = 0 To view.DataRowCount - 1
                Dim rowHandle = view.GetRowHandle(i)
                Dim rowView As DataRowView = view.GetRow(rowHandle)

                If rowView IsNot Nothing Then
                    Dim row As DataRow = rowView.Row
                    Dim value = row.Item(_DataColumn)

                    If Not (IsDBNull(value) OrElse String.IsNullOrWhiteSpace(value)) Then
                        If SelectedValues.Contains(value) Then
                            If MultiSelect Then
                                row.Item(0) = True
                            Else
                                view.SelectRow(rowHandle)
                            End If
                        End If
                    End If
                End If
            Next
        End If
    End Sub

    Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
        SaveSelectedValues()

        DialogResult = DialogResult.OK
        Close()
    End Sub

    Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
        SelectedValues = New List(Of String)

        DialogResult = DialogResult.OK
        Close()
    End Sub

    Private Sub gridLookup_KeyUp(sender As Object, e As KeyEventArgs) Handles gridLookup.KeyUp
        HandleCustomKeys(e)
    End Sub

    Private Sub gridLookup_EditorKeyUp(sender As Object, e As KeyEventArgs) Handles gridLookup.EditorKeyUp
        HandleCustomKeys(e)
    End Sub

    Private Sub HandleCustomKeys(e As KeyEventArgs)
        If e.KeyCode = Keys.Escape Then
            Close()
        ElseIf e.KeyCode = Keys.F2 Then
            ' Make sure the currently focused row's state is saved
            viewLookup.PostEditor()
            SaveSelectedValues()

            DialogResult = DialogResult.OK
            Close()
        End If
    End Sub

    Private Sub viewLookup_CellValueChanging(sender As Object, e As Views.Base.CellValueChangedEventArgs) Handles viewLookup.CellValueChanging
        If e.RowHandle = GridControl.NewItemRowHandle Then
            Exit Sub
        End If

        Dim rowHandle = e.RowHandle
        Dim column = e.Column
    End Sub

    Private Sub viewLookup_ShowingEditor(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles viewLookup.ShowingEditor
        Dim rowHandleIsNewItemRow = (_View.FocusedRowHandle = GridControl.NewItemRowHandle)
        Dim columnIsCheckboxColumn = (_View.FocusedColumn.FieldName = COLUMN_SELECTED)

        ' Prevent editing of Data Column/allow editing for Checkbox Column and NewValue Row
        If rowHandleIsNewItemRow Or columnIsCheckboxColumn Then
            e.Cancel = False
        Else
            e.Cancel = True
        End If
    End Sub

    Private Sub viewLookup_RowClick(sender As Object, e As RowClickEventArgs) Handles viewLookup.RowClick
        ' If user double-clicks on a row
        If e.Clicks = 2 And e.Button = MouseButtons.Left Then
            ' And clicked row is a normal row
            If e.RowHandle >= 0 Then
                ' If multiselect is true, check the current row
                ' If multiselect is false, select the current row and close the window
                If MultiSelect = True Then
                    Dim row As DataRow = _View.GetDataRow(e.RowHandle)
                    row.Item(0) = Not CBool(row.Item(0))
                Else
                    Dim row As DataRow = _View.GetDataRow(e.RowHandle)
                    Dim value = row.Item(0)

                    SelectedValues = New List(Of String) From {value}

                    DialogResult = DialogResult.OK
                    Close()
                End If
            End If
        End If
    End Sub

    Private Sub frmLookupGrid_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        BringToFront()
    End Sub

    Private Sub viewLookup_ValidateRow(sender As Object, e As ValidateRowEventArgs) Handles viewLookup.ValidateRow
        If e.RowHandle = GridControl.NewItemRowHandle Then
            Dim oRowView As DataRowView = viewLookup.GetRow(e.RowHandle)
            Dim oValue = GetValueFromRow(oRowView.Row)
            NewValues.Add(oValue)

            ' Automatically select newly added row when MultiSelect is enabled
            If MultiSelect Then
                oRowView.Row.Item(COLUMN_SELECTED) = True
            End If
        End If
    End Sub

    Private Sub viewLookup_FocusedRowChanged(sender As Object, e As FocusedRowChangedEventArgs) Handles viewLookup.FocusedRowChanged
        If AddNewValues AndAlso e.PrevFocusedRowHandle = GridControl.NewItemRowHandle Then
            BeginInvoke(Sub() viewLookup.FocusedRowHandle = GridControl.NewItemRowHandle)
        End If
    End Sub
End Class