Imports DevExpress.Utils Imports DevExpress.XtraEditors Imports DevExpress.XtraEditors.Repository Imports DevExpress.XtraGrid.Columns Imports DevExpress.XtraGrid.Views.Grid Imports DigitalData.Controls.LookupGrid Imports DigitalData.Modules.EDMI.API.Constants Imports DigitalData.Modules.EDMI.API.DatabaseWithFallback Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Base Imports System.ComponentModel Imports DevExpress.XtraEditors.Controls Imports DevExpress.XtraGrid.Views.Base Imports System.Text.RegularExpressions Namespace ControlCreator Public Class GridControl Private ReadOnly _LogConfig As LogConfig Private ReadOnly _Logger As Logger Private ReadOnly _GridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)) Private newRowModified As Boolean Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))) _LogConfig = pLogConfig _Logger = pLogConfig.GetLogger() _GridTables = pGridTables End Sub Public Function CreateGridColumns(pColumnTable As DataTable) As DataTable Dim oDataTable As New DataTable For Each oRow As DataRow In pColumnTable.Rows ' Create Columns in Datatable Dim oColumn = New DataColumn() With { .ColumnName = oRow.Item("SPALTENNAME"), .Caption = oRow.Item("SPALTEN_HEADER_LANG"), .ReadOnly = False } Select Case oRow.Item("TYPE_COLUMN") Case Constants.CONTROL_TYPE_TEXT oColumn.DataType = GetType(String) Case Constants.CONTROL_TYPE_INTEGER oColumn.DataType = GetType(Integer) Case Constants.CONTROL_TYPE_DOUBLE oColumn.DataType = GetType(Double) Case Constants.CONTROL_TYPE_CURRENCY oColumn.DataType = GetType(Double) Case Constants.CONTROL_TYPE_BOOLEAN oColumn.DataType = GetType(Boolean) Case Else oColumn.DataType = GetType(String) End Select oDataTable.Columns.Add(oColumn) Next Return oDataTable End Function Public Function FillGridTables(pColumnTable As DataTable, pControlId As Integer, pControlName As String) As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)) For Each oRow As DataRow In pColumnTable.Rows ' Fetch and cache Combobox results Dim oConnectionId As Integer = oRow.ItemEx("CONNECTION_ID", 0) Dim oSqlCommand As String = oRow.ItemEx("SQL_COMMAND", "") If oConnectionId > 0 And oSqlCommand <> "" Then Try Dim oComboboxDataTable As DataTable = Nothing Dim oColumnName As String = oRow.Item("SPALTENNAME") _LOGGER.Debug("Working on SQL for Column[{0}]...", oColumnName) If Not clsPatterns.HasComplexPatterns(oSqlCommand) Then _LOGGER.Debug("SQL has no complex patterns!") 'oComboboxDataTable = ClassDatabase.Return_Datatable_ConId(oSqlCommand, oConnectionId) oComboboxDataTable = DatabaseFallback.GetDatatable(New GetDatatableOptions(oSqlCommand, DatabaseType.ECM) With { .ConnectionId = oConnectionId }) Else _LOGGER.Debug("...has complex patterns!!") End If Dim oRepositoryItem = GridTables_GetRepositoryItemForColumn(oColumnName, oComboboxDataTable, oRow.Item("ADVANCED_LOOKUP")) If _GridTables.Item(pControlId).ContainsKey(oColumnName) Then _GridTables.Item(pControlId).Item(oColumnName) = oRepositoryItem Else _GridTables.Item(pControlId).Add(oColumnName, oRepositoryItem) End If Catch ex As Exception _LOGGER.Warn("Could not load data for column {0} in control {1}", oRow.Item("SPALTENNAME"), pControlName) _LOGGER.Error(ex) End Try End If Next Return _GridTables End Function Private Function GridTables_GetRepositoryItemForColumn(pColumnName As String, pDataTable As DataTable, pIsAdvancedLookup As Boolean) As RepositoryItem If pIsAdvancedLookup Then Dim oEditor = New RepositoryItemLookupControl3 If pDataTable IsNot Nothing Then oEditor.DisplayMember = pDataTable.Columns.Item(0).ColumnName oEditor.ValueMember = pDataTable.Columns.Item(0).ColumnName oEditor.DataSource = pDataTable End If Return oEditor Else Dim oEditor = New RepositoryItemComboBox() Dim oItems As New List(Of String) AddHandler oEditor.Validating, Sub(_sender As ComboBoxEdit, _e As CancelEventArgs) If oItems.Contains(_sender.EditValue) Then _e.Cancel = False Else _e.Cancel = True End If End Sub If pDataTable IsNot Nothing Then For Each oRow2 As DataRow In pDataTable.Rows Dim oValue = oRow2.Item(0) Try If oRow2.ItemArray.Length > 1 Then oValue &= $" | {oRow2.Item(1)}" End If Catch ex As Exception End Try oEditor.Items.Add(oValue) oItems.Add(oValue) Next End If Return oEditor End If End Function Public Sub ConfigureViewColumns(pColumnTable As DataTable, pGridView As GridView) Dim oShouldDisplayFooter As Boolean = False For Each oCol As GridColumn In pGridView.Columns Dim oColumnData As DataRow = pColumnTable. Select($"SPALTENNAME = '{oCol.FieldName}'"). FirstOrDefault() If oColumnData Is Nothing Then Continue For End If Dim oSequence As Integer = oColumnData.Item("SEQUENCE") oCol.VisibleIndex = oSequence Dim oColumnType As String = oColumnData.Item("TYPE_COLUMN") Select Case oColumnType Case "INTEGER" oCol.DisplayFormat.FormatType = FormatType.Custom oCol.DisplayFormat.FormatString = "N0" Case "DOUBLE" oCol.DisplayFormat.FormatType = FormatType.Custom oCol.DisplayFormat.FormatString = "N2" Case "CURRENCY" oCol.DisplayFormat.FormatType = FormatType.Custom oCol.DisplayFormat.FormatString = "C2" End Select Dim oSummaryFunction As String = oColumnData.Item("SUMMARY_FUNCTION") Select Case oSummaryFunction Case Constants.AGGREGATE_TOTAL_INTEGER oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum oCol.SummaryItem.DisplayFormat = "SUM: {0:N0}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_FLOAT oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum oCol.SummaryItem.DisplayFormat = "SUM: {0:N2}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_CURRENCY oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum oCol.SummaryItem.DisplayFormat = "SUM: {0:C2}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_AVG oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Average oCol.SummaryItem.DisplayFormat = "AVG: {0}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_MAX oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Max oCol.SummaryItem.DisplayFormat = "MAX: {0}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_MIN oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Min oCol.SummaryItem.DisplayFormat = "MIN: {0}" oShouldDisplayFooter = True Case Constants.AGGREGATE_TOTAL_COUNT oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Count oCol.SummaryItem.DisplayFormat = "NUM: {0}" oShouldDisplayFooter = True End Select Next pGridView.OptionsView.ShowFooter = oShouldDisplayFooter End Sub Public Sub ConfigureViewEvents(pColumnTable As DataTable, pGridView As GridView, pControl As Windows.Forms.Control, pControlId As Integer) AddHandler pGridView.InitNewRow, Sub(sender As Object, e As InitNewRowEventArgs) Try _LOGGER.Debug("Initialzing new row") For Each oColumnData As DataRow In pColumnTable.Rows For Each oGridColumn As GridColumn In pGridView.Columns If oGridColumn.FieldName <> oColumnData.Item("SPALTENNAME") Then Continue For End If Dim oDefaultValue = ObjectEx.NotNull(oColumnData.Item("DEFAULT_VALUE"), String.Empty) If oDefaultValue <> String.Empty Then _LOGGER.Debug("Setting default value [{0}] for column [{1}]", oDefaultValue, oGridColumn.FieldName) pGridView.SetRowCellValue(e.RowHandle, oGridColumn.FieldName, oDefaultValue) End If Next Next Catch ex As Exception _LOGGER.Error(ex) Finally newRowModified = False End Try End Sub AddHandler pGridView.CustomRowCellEdit, Sub(sender As Object, e As CustomRowCellEditEventArgs) Try For Each oRow As DataRow In pColumnTable.Rows Dim oColumnName = oRow.Item("SPALTENNAME") Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName) If oColumnName <> e.Column.FieldName Then Continue For End If If oEditorExists Then Dim oEditor = _GridTables.Item(pControlId).Item(oColumnName) _LOGGER.Debug("Assigning Editor to Column [{0}]", oColumnName) e.RepositoryItem = oEditor Else _LOGGER.Debug("Editor for Column [{0}] does not exist", oColumnName) End If Next Catch ex As Exception _LOGGER.Warn("Error in CustomRowCellEdit for [{0}]", e.CellValue) _LOGGER.Error(ex) End Try End Sub AddHandler pGridView.ValidatingEditor, Sub(sender As Object, e As BaseContainerValidateEditorEventArgs) Dim oRow As DataRowView = pGridView.GetRow(pGridView.FocusedRowHandle) Dim oColumnName = pGridView.FocusedColumn.FieldName _LOGGER.Debug("Validating Editor for Column [{0}]", oColumnName) GridTables_ValidateColumn(pGridView, pColumnTable, oColumnName, e.Value, e.Valid, e.ErrorText) End Sub AddHandler pGridView.PopupMenuShowing, AddressOf View_PopupMenuShowing AddHandler pGridView.InvalidRowException, AddressOf View_InvalidRowException AddHandler pGridView.ValidatingEditor, AddressOf View_ValidatingEditor ' These handlers are all used for the custom DefaultValue functionality, additionally some code in the 'InitNewRow' event. ' https://supportcenter.devexpress.com/ticket/details/t1035580/how-to-default-a-value-in-a-column-when-add-new-row-in-data-grid AddHandler pGridView.ShowingEditor, AddressOf View_ShowingEditor AddHandler pGridView.ShownEditor, AddressOf View_ShownEditor AddHandler pGridView.ValidateRow, AddressOf View_ValidateRow AddHandler pControl.LostFocus, AddressOf Control_LostFocus End Sub Private Sub View_PopupMenuShowing(sender As Object, e As PopupMenuShowingEventArgs) Dim view As GridView = TryCast(sender, GridView) Dim oFocusedColumn As GridColumn = view.FocusedColumn Dim oColumnType As Type = oFocusedColumn.ColumnType Dim oColumnName As String = oFocusedColumn.FieldName If e.MenuType = GridMenuType.Column AndAlso oColumnType Is GetType(Boolean) Then e.Menu.Items.Add(New Menu.DXMenuItem( "Alle Zeilen anhaken", Sub(_sender As Object, _e As EventArgs) SetCellValuesForBooleanColumn(view, oColumnName, True) End Sub, My.Resources.itemtypechecked, Menu.DXMenuItemPriority.Normal)) e.Menu.Items.Add(New Menu.DXMenuItem( "Alle Zeilen abhaken", Sub(_sender As Object, _e As EventArgs) SetCellValuesForBooleanColumn(view, oColumnName, False) End Sub, My.Resources.itemtypechecked, Menu.DXMenuItemPriority.Normal)) End If End Sub Private Sub SetCellValuesForBooleanColumn(pView As GridView, pColumnName As String, pValue As Boolean) Dim oRowHandle = 0 While (pView.IsValidRowHandle(oRowHandle)) Dim oRow = pView.GetDataRow(oRowHandle) Dim oValue = oRow.ItemEx(pColumnName, False) oRow.Item(pColumnName) = pValue oRowHandle += 1 End While End Sub Private Sub Control_LostFocus(sender As DevExpress.XtraGrid.GridControl, e As EventArgs) Dim oView2 As GridView = sender.FocusedView ' 19.08.2022: ' Calling UpdateCurrentRow when newRowModified Is true ' leads to some weird jumping of focus in the current cell. ' This seems to fix it. If newRowModified = False Then oView2.UpdateCurrentRow() End If End Sub Private Sub View_ShowingEditor(sender As Object, e As CancelEventArgs) Dim view As GridView = TryCast(sender, GridView) _LOGGER.Debug("Showing editor.") If view.IsNewItemRow(view.FocusedRowHandle) AndAlso Not newRowModified Then _LOGGER.Debug("Adding new row.") view.AddNewRow() End If End Sub Private Sub View_ShownEditor(sender As Object, e As EventArgs) Dim view As GridView = TryCast(sender, GridView) If view.IsNewItemRow(view.FocusedRowHandle) Then _LOGGER.Debug("Attaching Modified Handler.") AddHandler view.ActiveEditor.Modified, Sub() _LOGGER.Debug("Row was modified.") newRowModified = True End Sub End If End Sub Private Sub View_ValidateRow(sender As Object, e As ValidateRowEventArgs) Dim view As GridView = TryCast(sender, GridView) If view.IsNewItemRow(e.RowHandle) AndAlso Not newRowModified Then _LOGGER.Debug("Deleting unused row") view.DeleteRow(e.RowHandle) End If _LOGGER.Debug("Validating row. Resetting Modified.") newRowModified = False End Sub Private Sub View_ValidatingEditor(sender As Object, e As BaseContainerValidateEditorEventArgs) Dim oValue As String = ObjectEx.NotNull(e.Value, "") If oValue.Contains(" | ") Then oValue = oValue.Split(" | ").ToList().First() e.Value = oValue End If End Sub Private Sub View_InvalidRowException(sender As Object, e As InvalidRowExceptionEventArgs) e.ExceptionMode = ExceptionMode.NoAction End Sub Private Function GridTables_TestEditorExistsByControlAndColumn(oControlId As Integer, pColumn As String) As Boolean If _GridTables.ContainsKey(oControlId) Then Dim oContainsKey = _GridTables.Item(oControlId).ContainsKey(pColumn) If oContainsKey AndAlso _GridTables.Item(oControlId).Item(pColumn) IsNot Nothing Then Return True Else Return False End If Else Return False End If End Function Private Function GridTables_ValidateColumn(pView As GridView, pColumnDefinition As DataTable, ColumnName As String, pValue As Object, ByRef pIsValid As Boolean, ByRef pErrorText As String) As Boolean Dim oColumn As DataRow = (From r As DataRow In pColumnDefinition.Rows Where r.Item("SPALTENNAME") = ColumnName Select r).FirstOrDefault() Dim oGridColumn As GridColumn = (From c As GridColumn In pView.Columns Where c.FieldName = ColumnName Select c).FirstOrDefault() Dim oIsRequired = oColumn.Item("VALIDATION") Try Dim oRegex = ObjectEx.NotNull(oColumn.Item("REGEX_MATCH"), String.Empty) Dim oRegexMessage = ObjectEx.NotNull(oColumn.Item("REGEX_MESSAGE_DE"), String.Empty) If oRegex <> String.Empty Then Dim oMatch = New Regex(oRegex).IsMatch(pValue.ToString) Dim oDefaultMessage = "Wert entspricht nicht dem gefordertem Format!" Dim oMessage = IIf(oRegexMessage <> String.Empty, oRegexMessage, oDefaultMessage) If oMatch = False Then pErrorText = oMessage pIsValid = False Return False End If End If Catch ex As Exception _LOGGER.Error(ex) End Try If oIsRequired And (pValue IsNot Nothing AndAlso pValue.ToString = "") Then pErrorText = "Spalte muss ausgefüllt werden!" pIsValid = False Return False End If Return True End Function End Class End Namespace