Imports System.Data 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 ReadOnly _R As Resources.ResourceManager = My.Resources.Strings.ResourceManager Private colCheckbox As GridColumn #Region "Form Events" 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 colCheckbox = _View.Columns.Item(0) 'oCheckboxColumn.Visible = False colCheckbox.Caption = " " colCheckbox.MaxWidth = 30 colCheckbox.MinWidth = 30 colCheckbox.OptionsColumn.AllowFocus = False Text = _R.GetString("PopupForm_TextMultiselect") _DataColumn = 1 Else Text = _R.GetString("PopupForm_Text") _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 Try If _View.Columns.Count = 0 Then Dim oDataColumn As GridColumn = _View.Columns.Item(_DataColumn) oDataColumn.BestFit() End If Catch ex As ArgumentOutOfRangeException Catch ex As Exception End Try End Sub Private Sub frmLookupGrid_Shown(sender As Object, e As EventArgs) Handles Me.Shown BringToFront() End Sub #End Region #Region "Button Events" Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click SaveAndClose() End Sub Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click ClearAndClose() End Sub Private Sub ClearAndClose() SelectedValues = New List(Of String) DialogResult = DialogResult.OK Close() End Sub Private Sub SaveAndClose() ' Make sure the currently focused row's state is saved viewLookup.PostEditor() SaveSelectedValues() DialogResult = DialogResult.OK Close() End Sub #End Region #Region "Grid Events" 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) Select Case e.KeyCode Case Keys.Escape Close() Case Keys.F2 SaveAndClose() Case Keys.Back ClearAndClose() Case Keys.Space If MultiSelect Then Dim oHandle = viewLookup.FocusedRowHandle If oHandle >= 0 Then Dim oIsChecked = viewLookup.GetRowCellValue(oHandle, colCheckbox) If oIsChecked Then viewLookup.SetRowCellValue(oHandle, colCheckbox, False) Else viewLookup.SetRowCellValue(oHandle, colCheckbox, True) End If End If End If End Select End Sub #End Region #Region "View Events" 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 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_RowCellClick(sender As Object, e As RowCellClickEventArgs) Handles viewLookup.RowCellClick If e.RowHandle = GridControl.InvalidRowHandle Or e.RowHandle = GridControl.NewItemRowHandle Then e.Handled = False Exit Sub End If ' When AllowFocus is used on the SELECTED Column, the checkbox can only be selected with a double click ' This function manually checks/unchecks the clicked cell If MultiSelect AndAlso e.Column.FieldName = COLUMN_SELECTED Then Dim row As DataRow = _View.GetDataRow(e.RowHandle) row.Item(0) = Not CBool(row.Item(0)) End If End Sub Private Sub viewLookup_FocusedRowChanged(sender As Object, e As FocusedRowChangedEventArgs) Handles viewLookup.FocusedRowChanged ' Removed because it leads to some weird behaviour where you a locked in the new row forever 'If AddNewValues AndAlso e.PrevFocusedRowHandle = GridControl.NewItemRowHandle Then ' BeginInvoke(Sub() viewLookup.FocusedRowHandle = GridControl.NewItemRowHandle) 'End If End Sub #End Region 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 ' Converting to string explicitly to prevent DBNull crashing the function If MultiSelect Then Return pRow.Item(1).ToString() Else Return pRow.Item(0).ToString() 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 End Class