Making Infragistics UltraWinGrid's Specific Columns Read-only - vb.net

I am stuck in a situation where I need to disable a few columns in each row, except for the newly added row.
That is, I have 10 columns in the grid and the first 3 columns are databound and come from the database as disabled or read-only. The rest are editable.
If I add a new row, then all the columns of the new row must be enabled until it is saved.
I don't have any DataKey or primary key for my existing row or the new row. I have to check for some boolean value like IsNewRow.
In my current scenario I am using this code block:
Private Sub dgTimeSheet_InitializeRow(ByVal sender As Object, ByVal e As Infragistics.Win.UltraWinGrid.InitializeRowEventArgs) Handles dgTimeSheet.InitializeRow
''if either column key is Project, Class or Milestone
'' Activation.NoEdit = Disable and Activation.AllowEdit = Enable
Dim index As Integer = e.Row.Index
If e.Row.IsAddRow Then
dgTimeSheet.Rows(index).Cells(PROJECT).Activation = Activation.AllowEdit
dgTimeSheet.Rows(index).Cells(SERVICE_ISSUE_CLASS).Activation = Activation.AllowEdit
dgTimeSheet.Rows(index).Cells(MILESTONE).Activation = Activation.AllowEdit
Else
dgTimeSheet.Rows(index).Cells(PROJECT).Activation = Activation.NoEdit
dgTimeSheet.Rows(index).Cells(SERVICE_ISSUE_CLASS).Activation = Activation.NoEdit
dgTimeSheet.Rows(index).Cells(MILESTONE).Activation = Activation.NoEdit
End If
CheckRows()
End Sub
The problem is that if I click on disabled/read-only rows, then newly added rows also get disabled, which I don't want.

I am fighting with a similar problem in C#, so this is fishing in the dark... Is it possible, in your case, to add an IgnoreRowColActivation = true statement to keep the rows from reverting?

This examples create read only columns for the interface a customer uses
Example for a row with three columns. Set two columns as readonly and the third as editable to the user.
the three columns defined in the designer:
System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
set column 1 & 2 directly
this.dataGridViewTextBoxColumn1.ReadOnly = true
this.dataGridViewTextBoxColumn2.ReadOnly = true
You can still update all columns in the source code. The customer will only be able to edit the third column.

Related

If DataRow exists in DataTable, update values within it

Each time a user changes a value in one of the editable columns within my DataGridView, it triggers a recalculation of up to 8 fields. This means the event CellValueChanged Event on the DataGridView will trigger more than 1 time.
I want to create a row in a DataSet.DataTable which will include the new values for all columns after the user has changed a value.
The issue I am having is that the code below creates a row for each column that has had its value changed due to the recalculated columns.
Private Sub PL_DGV_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles PL_DGV.CellValueChanged
If isLoaded Then
Dim grid_row As DataRow = Me.DataSet.PL.Rows(e.RowIndex)
Dim unsaved_row As DataRow = grid_row
Me.DataSet.PL_UnsavedChanges.ImportRow(unsaved_row)
unsaved_changes = True
End If
End Sub
How could I make it so that if a row already exists, it will just update the values within it each time?
There is no PrimaryKeys assigned to the DataTable within the DataSet, but there are 3 columns which would be used as unique values per row.
[UPDATE BASED ON COMMENTS]
With the code shown above what will happen is that the user will update a value in column 1 >
Event is triggered and new row is created which will include the new value in Column 1 >
Column 2 is recalculated based on changes in Column 1 >
Event is triggered and new row is created which will include the new value in Column 1 and Column 2 >
Column 3 is recalculated based on changes on either of the previously mentioned columns >
Event is triggered and new row is created which will include new value in Column 1, Column 2 and Column 3.
What I want to achieve is to have 1 row which will have all the new values, no need to generate any other rows but one which includes the changes from the user action and any recalculations based on that.
After the discussion within the comments the solution to my issue, or rather what I did to overcome it was the below:
Dim grid_row As DataRow = Me.DataSet.PL.Rows(e.RowIndex)
Dim Column1 = Variable1
Dim Column2 = grid_row.Item("Column2").ToString().Trim()
Dim Column3 = grid_row.Item("Column3").ToString().Trim()
Dim Updated_column_name = Me.DataSet.PL.Columns(e.ColumnIndex).ColumnName
Dim Updated_value = grid_row.Item(Updated_column_name).ToString()
Dim row As DataRow = Me.DataSet.PL_ChangesLog.NewRow()
row("price_list") = Price_list
row("warehouse") = Warehouse
row("product_code") = Product
row("column_name") = Updated_column_name
row("updated_value") = Updated_value
row("timestamp") = DateTime.Now()
row("username") = Environment.UserName()
Me.DataSet.PL_ChangesLog.Rows.Add(row)
unsaved_changes = True
This allows me to create a DataTable holding a row of data for each cell that has had its value updated, whether recalculated via code or by user action.
Based on that I can pick out the last audit and use that as the "Save Changes" to commit the changes to the SQL Database.

SQL Update not working after using SQL to build columns in a bound DataGridView-VB

I am Using VB Windows Forms with a databound DataGridView control. Using Access, I have a "Customers" table and am using SQL commands to populate selected fields to the DataGridView. PROBLEM: I have an "Active" field with 1 for active and 0 for inactive. In the DataGridView window I need to change the "Active" column to display Y for active and N for inactive (without changing the underlying data). I have tried to use the UPDATE command without success. After the first SQL commands to populate the DataGridView, I'm not sure how to code the second SQL command to modify the "Active" column.
Don't do anything in the SQL. The grid can handle it for you, with a bit of code.
You need to handle the CellFormatting and CellParsing events of the grid and first confirm that it is that column. When formatting, you convert from 1 and 0 to "Y" and "N" respectively and, when parsing, you do the inverse. E.g.
Private activeColumnIndex As Integer 'Set column index as appropriate.
Private Sub DataGridView1_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
If e.ColumnIndex = activeColumnIndex Then
Select Case CInt(e.Value)
Case 0
e.Value = "N"
Case 1
e.Value = "Y"
End Select
e.FormattingApplied = True
End If
End Sub
Private Sub DataGridView1_CellParsing(sender As Object, e As DataGridViewCellParsingEventArgs) Handles DataGridView1.CellParsing
If e.ColumnIndex = activeColumnIndex Then
Select Case CStr(e.Value)
Case "N"
e.Value = 0
Case "Y"
e.Value = 1
End Select
e.ParsingApplied = True
End If
End Sub
Note that this code doesn't account for NULL values or invalid input. That's out of scope and up to you to implement.
Thinking about this further, I'd suggest that you should actually use a combo box column, rather than a text box column. That way, the user can't possibly enter an invalid value, because they have to select either "Y" or "N" from the drop-down list.
A DataGridView will never create a combo box column for you though, so you need to add one yourself. You can do it in code but I would suggest that you do it in the designer. Either way, you need to set the DataPropertyName property of the grid column to the name of the data source column ("Active" in your case) to make it bind correctly. See my guide to adding a combo box column in the designer here.
Once you have your combo box column, you need to bind the drop-down items. In your case, you can do this:
Dim activeOptions = {New With {.Text = "Y", .Value = 1},
New With {.Text = "N", .Value = 0}}
With activeColumn
.DisplayMember = "Text"
.ValueMember = "Value"
.DataSource = activeOptions
End With
You should do that before you set the DataSource of the grid. That code creates an array of objects of an anonymous type where the Text values are what get displayed in the drop-down list and the Value values correspond to the cell values from the grid's data source.

Using LINQ to find updated rows in DataTable

I'm building an application in VB.NET where I am pushing data from one database to another. The source database is SQL Server and the target is MySQL.
What I am doing is first creating DataTables for each table in each database which I use to do a comparison. I've written the queries in such a way so that the source and target DataTables contain exactly the same columns and values to make the comparison easier.
This side of the application works fine. What I do next is find rows which do not exist in the target database by finding PKs which do not exist. I then insert these new rows into the target database with no problem.
The Problem
What I now need to do is find rows in each table that have been updated, i.e. are not identical to the corresponding rows in the target DataTable. I have tried using Except() as per the example below:
Public Function GetUpdates(ByVal DSDataSet As MSSQLQuery, ByVal AADataSet As MySQLQuery, Optional ByVal PK As String = Nothing) As List(Of DataRow)
' Determines records to be updated in the AADB and returns list of new Rows
' Param DSDataSet - MSSQLQuery Object for source table
' Param AADataSet - MySQLQuery Object for destination table
' Optional Param PK - String of name common columns to treat as PK
' Returns List(Of DataRow) containing rows to update in table
Dim orig = DSDataSet.GetDataset()
Dim origTable = orig.Tables(0).AsEnumerable()
Dim destination = AADataSet.GetDataset()
Dim destinationTable = destination.Tables(0).AsEnumerable()
' Get Records which are not in destination table
Dim ChangedRows = Nothing
If IsNothing(PK) Then
ChangedRows = destinationTable.AsEnumerable().Except(origTable.AsEnumerable(), DataRowComparer.Default)
End If
Dim List As New List(Of DataRow)
For Each addRow In ChangedRows
List.Add(addRow)
Next
Return List
End Function
The trouble is that it ends up simply returning the entire set of source rows.
How can I check for these changed rows? I could always hardcode queries to return what I want but this introduces problems because I need to make comparisons for 15 tables so it would be a complete mess.
Ideally I need a solution where it will take into account the variable number columns from the source tables for comparison against what is essentially an identical target table and simply compare the DataRows for equality.
There should be a corresponding row in the target tables for every source row since the addition of new rows is performed prior to this check for updated rows.
I am also open to using methods other than LINQ to achieve this.
Solution
In the end I implemented a custom comparer to use in the query as shown below. It first checks if the first column value matches (PK in my case) where if it does then it we check column-wise that everything matches.
Any discrepancy will set the flag value to FALSE which we return. If there aren't any issues then TRUE will be returned. In this case I used = to compare equality between values rather than Equals() since I'm not concerned about a strict equality.
The resulting set of DataRows is used to UPDATE the database using the first column value (PK) in the WHERE clause.
Imports System.Data
Class MyDataRowComparer
Inherits EqualityComparer(Of DataRow)
Public Overloads Overrides Function Equals(x As DataRow, y As DataRow) As Boolean
If x.Item(0).ToString().Equals(y.Item(0).ToString()) Then
' If PK matches then check column-wise.
Dim Flag As Boolean = True
For Counter As Integer = 0 To x.ItemArray.Count - 1
If Not x.Item(Counter) = y.Item(Counter) Then
Flag = False
End If
Next
Return Flag
Else
' Otherwise don't bother and just skip.
Return False
End If
End Function
...
End Class
class MyDataRowComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
return x["ColumnName"].Equals(y["ColumnName"]);
// Can add more columns to the Comparison
}
public int GetHashCode(DataRow obj)
{
return obj["ColumnName"].GetHashCode();
// Can add more columns to calculate HashCode
}
}
Now the Except statement will be like:
ChangedRows = destinationTable.AsEnumerable()
.Except(origTable.AsEnumerable(), MyDataRowComparer)

DataGridView not showing all columns in data set

I have a simple Dataset viewer app that shows the contents of a dataset. My dataset has many datatables each with many rows. Unfortunately one of the tables is only showing the first column. When I look at the datatable in the debugger I see all the rows, when it's displayed in the datagridview only the one row. There doesn't seem to be anything weird about the column names.
Here's the code that sets the datagridview:
' Load datagrid
With DataGridViewXml
.AutoGenerateColumns = True
.DataSource = ssrsReportDataSet
.DataMember = ssrsReportDataSet.Tables(0).TableName
End With
' Table at index 4 is the one losing the columns
tableSelecter.SelectedIndex = 4
Here's how I update the view with the table I need:
' Load datagrid
With DataGridViewXml
.DataMember = ssrsReportDataSet.Tables("MyTable").TableName
End With
Instead of using the DataMember at all, why not do this:
DataGridViewXml.DataSource = ssrsReportDataSet.Tables(0)
and then this:
DataGridViewXml.DataSource = ssrsReportDataSet.Tables("MyTable")
There's no point setting AutoGenerateColumns because it's True by default. You may or may not need to set the DataSource to Nothing and Clear the Columns collection in between.

DevExpress XtraGrid GroupRow, CheckEdit Interactions Possibilities

Currently, I am displaying an XtraGrid that contains Group Rows. I have a "Select All" DevExpress.XtraEditors.CheckEdit control (which is different from this elusive "Select All" check box control I am reading about in the documentation). It is different for a reason: I want the check box to do something other than "Select All" (which comes in only three different varieties according to the DevExpress Docs.).
I want a user to be able to do one of two things with the CheckEdit control. [1] If no Group Rows are expanded, I want to select all Group Rows. [2] If one or more Group Rows are expanded, I only want to select the expanded rows.
Presently, I am able to manipulate the controls to do only one of the two things (see code). My question is twofold: is this possible; and, if so, how would I go about it?
Here is my code that does the second of the two 'things' described above:
'If the CheckEdit control is checked:
xtraGrid.SelectAll()
Dim rowHandles() As Int32 = xtraGrid.GetSelectedRows()
If rowHandles.Count > 0 Then
For Each RowHandle As Int32 In rowHandles
If xtraGrid.IsGroupRow(RowHandle) Then
xtraGrid.UnselectRow(RowHandle)
End If
Next
End If
As you can see, all this really is just a work around. There is also presumably more overhead than needed in my calling .GetSelectedRows(). I am just attempting to sort through the row types in order to keep the row selected or .UnselectRow()
You can use GridView.GetRowExpanded method to check whether a specific group row is expanded. For iterating through visible rows you can use GridView.RowCount property. To get row level just use GridView.GetRowLevel method. Also you need to check wether a row is new item row or is filter row by using GridView.IsNewItemRow method and GridView.IsFilterRow method. For all of this methods you need to get Row handle by using GridView.GetVisibleRowHandle method. When you are working with selection is better to use GridView.BeginSelection method and GridView.EndSelection method outside of your code.
UPDATE: If you want to select hidden rows within a collapsed group then you can use GridView.GetChildRowCount method and GridView.GetChildRowHandle method to get all hidden rows.
Here is some sample code that is performing your two things together:
Private Sub SomeSub
If xtraGrid.GroupCount = 0 Then
xtraGrid.SelectAll()
Exit Sub
End If
xtraGrid.BeginSelection()
xtraGrid.ClearSelection()
Dim isExpanded As Boolean = False
For rowVisibleIndex = 0 To xtraGrid.RowCount - 1
Dim rowHandle As Integer = xtraGrid.GetVisibleRowHandle(rowVisibleIndex)
If xtraGrid.IsNewItemRow(rowHandle) Then
Continue For
End If
Dim level As Integer = xtraGrid.GetRowLevel(rowHandle)
If level = 0 Then
If Not isExpanded Then
isExpanded = xtraGrid.GetRowExpanded(rowHandle)
If isExpanded Then
xtraGrid.ClearSelection()
Else
xtraGrid.SelectRow(rowHandle)
End If
End If
Else
xtraGrid.SelectRow(rowHandle)
'Update: select hidden rows
If xtraGrid.IsGroupRow(rowHandle) And Not xtraGrid.GetRowExpanded(rowHandle) Then
SelectRowHierarchy(rowHandle)
End If
End If
Next
xtraGrid.EndSelection()
End Sub
Private Sub SelectRowHierarchy(rowHandle As Integer)
Dim childCount As Integer = xtraGrid.GetChildRowCount(rowHandle)
For childIndex As Integer = 0 To childCount - 1
Dim childRowHandle As Integer = xtraGrid.GetChildRowHandle(rowHandle, childIndex)
xtraGrid.SelectRow(childRowHandle)
If xtraGrid.IsGroupRow(childRowHandle) Then
SelectRowHierarchy(childRowHandle)
End If
Next
End Sub