Extended datagrid

G

Guest

I have a DataGrid control for which I have also created several new extended
DataGridColumnStyles. They behave pretty nicely, but I can't figure out how
to implement Selected Item formatting for them.

In a plain vanilla DataGrid, when you click on the RowHeader, the
appropriate row changes colors. I ASSUME this should be done in the Paint
(or PaintText) override of the DataGridColumnStyle in question. My problem
is that I don't know how to retrieve the DataGrid of the DataGridColumnStyle
instance to test if the row is selected.

Or, maybe this isn't the best way to do this. Any help?

Thanks,
 
M

Mike McIntyre

The DataGridColumnStyle class has a property named DataGridTableStyle which
in turn has a DataGrid property which holds a refererence the DataGrid
hosting the DataGridColumnStyle.

By default, your derived column should be colored (highlighted) when a row
is selected in the DataGrid. Is this not happening? Or am I
misunderstanding your question?


--
Mike

Mike McIntyre
Visual Basic MVP
www.getdotnetcode.com
 
G

Guest

By default, your derived column should be colored (highlighted) when a row
is selected in the DataGrid. Is this not happening?

No, this is NOT happening. Mind you, I have overridden a bunch of stuff in
my derived DataGridColumnStyles.
The DataGridColumnStyle class has a property named DataGridTableStyle which
in turn has a DataGrid property which holds a refererence the DataGrid
hosting the DataGridColumnStyle.

In fact I found the reference to the datagrid shortly after my first post.
"FORCING" the row to paint in the DataTableStyles.BackColor after testing for
selection doesn't work, i.e. the DataGrid thinks that the row in question is
not selected. It seems I have overridden something that would ordinarily set
the IsSelected for the row.

Does this ring any bells?

Thanks,

Pat
 
M

Mike McIntyre

No bells ringing yet.

If you can share your DataGridColumnStyle code post it here or send it to
me: (e-mail address removed)
and I will take a look.

Mike
 
G

Guest

"FORCING" the row to paint in the DataTableStyles.BackColor after testing
for
selection doesn't work, i.e. the DataGrid thinks that the row in question is
not selected. It seems I have overridden something that would ordinarily set
the IsSelected for the row.

This is not exactly true. While it IS true that clicking the rowheader for
row n does not appear to set datagrid.IsSelected(n), I'm not sure that is
what happens in the vanilla DataGrid. I replaced one of my extended columns
in my test form with a vanilla column and now when I select a row, the
vanilla cell is highlighted while my various extended cells are not.
 
M

Mike McIntyre

When the row header is clicked no particular cell is selected in the row.

When a cell is clicked, you now have an active CurrentCell in the DataGrid.

Just clarifying a bit...

When you click in a cell the colum
 
G

Guest

Here is the code for my basic ExtendedDataGridColumn as well as for an
inherited ExtendedComboBoxColumn.


#Region "ExtendedDataGridColumn"

Public MustInherit Class ExtendedDataGridColumnStyle
Inherits DataGridColumnStyle

#Region "Private"

#Region "Properties"

Private mxMargin As Integer = 2
Private myMargin As Integer = 1
Private WithEvents mcellControl As Control = Nothing
Private mText As String = ""
Private moldVal As String = String.Empty
Private minEdit As Boolean = False
Private mrowNum As Integer
Private WithEvents msource As CurrencyManager
Private mIsDirty As Boolean = False

Private ReadOnly Property DataGridTableGridLineWidth() As Integer
Get
If Me.DataGridTableStyle.GridLineStyle =
DataGridLineStyle.Solid Then
Return 1
Else
Return 0
End If
End Get
End Property

#End Region

#End Region

#Region "Protected"

#Region "Functions"

Protected Overrides Function GetPreferredHeight(ByVal g As
System.Drawing.Graphics, ByVal value As Object) As Integer
Dim NewLineIndex As Integer = 0
Dim NewLines As Integer = 0
Dim ValueString As String = Me.GetText(value)
Do
While NewLineIndex <> -1
NewLineIndex = ValueString.IndexOf("r\n",
NewLineIndex + 1)
NewLines += 1
End While
Loop

Return FontHeight * NewLines + myMargin
End Function

Protected Overrides Function GetPreferredSize(ByVal g As
System.Drawing.Graphics, ByVal value As Object) As System.Drawing.Size
Dim Extents As Size =
Size.Ceiling(g.MeasureString(GetText(value), _

Me.DataGridTableStyle.DataGrid.Font))
Extents.Width += mxMargin * 2 + DataGridTableGridLineWidth
Extents.Height += myMargin
Return Extents
End Function

Protected Overridable Function GetText(ByVal Value As Object) As
String
If Value Is System.DBNull.Value Then Return NullText

If Not Value Is Nothing Then
Return Value.ToString
Else
Return String.Empty
End If

End Function

Protected Overrides Sub ColumnStartedEditing(ByVal
editingControl As System.Windows.Forms.Control)
RaiseEvent ColumnEdited(Me, New System.EventArgs)
If (Not editingControl.Text = Me.OldValue) _
OrElse (editingControl.Text Is Nothing And Not Me.OldValue
Is Nothing) _
OrElse (Not editingControl.Text Is Nothing And Me.OldValue
Is Nothing) Then
RaiseEvent ColumnDirty(Me, New System.EventArgs)
End If
MyBase.ColumnStartedEditing(editingControl)
End Sub

Protected MustOverride Function GetColumnTextAtRow(ByVal Source
As CurrencyManager, _
ByVal RowNum
As Integer) As Object

#End Region

#Region "Properties"

Protected Property CellControl() As Control
Get
Return mcellControl
End Get
Set(ByVal Value As Control)
mcellControl = Value
End Set
End Property

Protected Property RowNumber() As Integer
Get
Return mrowNum
End Get
Set(ByVal Value As Integer)
mrowNum = Value
End Set
End Property

Protected Property GridDataSource() As CurrencyManager
Get
Return msource
End Get
Set(ByVal Value As CurrencyManager)
msource = Value
End Set
End Property

Protected Property InEdit() As Boolean
Get
Return minEdit
End Get
Set(ByVal Value As Boolean)
minEdit = Value
End Set
End Property

Protected ReadOnly Property yMargin() As Integer
Get
Return myMargin
End Get
End Property

Protected ReadOnly Property xMargin() As Integer
Get
Return mxMargin
End Get
End Property

Protected Property OldValue() As Object
Get
Return moldVal
End Get
Set(ByVal Value As Object)
If Not Value Is System.DBNull.Value Then
moldVal = Value
Else
moldVal = String.Empty
End If
End Set
End Property

Protected Property IsDirty() As Boolean
Get
Return mIsDirty
End Get
Set(ByVal Value As Boolean)
If Not mIsDirty = False Then
mIsDirty = Value
RaiseEvent ColumnDirty(Me, New System.EventArgs)
End If
End Set
End Property

#End Region

#Region "Subroutines"

Protected Overrides Sub Abort(ByVal rowNum As Integer)
RollBack()
EndEdit()
Invalidate()
End Sub

Protected Overloads Overrides Sub Paint(ByVal g As
System.Drawing.Graphics, _
ByVal bounds As
System.Drawing.Rectangle, _
ByVal source As
System.Windows.Forms.CurrencyManager, _
ByVal rowNum As Integer)

Paint(g, bounds, source, rowNum, False)

End Sub

Protected Overloads Overrides Sub Paint(ByVal g As
System.Drawing.Graphics, _
ByVal bounds As
System.Drawing.Rectangle, _
ByVal source As
System.Windows.Forms.CurrencyManager, _
ByVal rowNum As Integer, _
ByVal alignToRight As
Boolean)

Dim Text As String = GetText(GetColumnTextAtRow(source,
rowNum))
'Dim BackBrush As Brush
'Dim ForeBrush As Brush
'If Me.DataGridTableStyle.DataGrid.IsSelected(rowNum) Then
' BackBrush = New
SolidBrush(Me.DataGridTableStyle.SelectionBackColor)
' ForeBrush = New
SolidBrush(Me.DataGridTableStyle.ForeColor)
'Else
' BackBrush = New
SolidBrush(Me.DataGridTableStyle.BackColor)
' ForeBrush = New
SolidBrush(Me.DataGridTableStyle.ForeColor)
'End If
PaintText(g, bounds, Text, alignToRight)
'PaintText(g, bounds, Text, BackBrush, ForeBrush,
alignToRight)
End Sub

Protected Overloads Sub Paint(ByVal g As Graphics, _
ByVal Bounds As Rectangle, _
ByVal Source As CurrencyManager, _
ByVal RowNum As Integer, _
ByVal BackBrush As Brush, _
ByVal ForeBrush As Brush, _
ByVal AlignToRight As Boolean)

Dim Text As String = GetText(GetColumnTextAtRow(Source,
RowNum))
PaintText(g, Bounds, Text, BackBrush, ForeBrush, AlignToRight)
End Sub

Protected Overloads Overrides Sub SetDataGridInColumn(ByVal
Value As DataGrid)
MyBase.SetDataGridInColumn(Value)
If Not (mcellControl.Parent Is Value) Then
If Not (mcellControl.Parent Is Nothing) Then
mcellControl.Parent.Controls.Remove(mcellControl)
End If
End If

If Not (Value Is Nothing) Then
Value.Controls.Add(mcellControl)

End Sub

Protected Overloads Overrides Sub UpdateUI(ByVal Source As
CurrencyManager, _
ByVal RowNum As
Integer, ByVal InstantText As String)
mcellControl.Text = GetText(GetColumnTextAtRow(Source,
RowNum))
If Not (InstantText Is Nothing) Then
mcellControl.Text = InstantText
End If
End Sub

Protected Overridable Sub EndEdit()
minEdit = False
Invalidate()
End Sub

Protected Overridable Sub RollBack()
mcellControl.Text = moldVal
End Sub

Protected Overridable Sub PaintText(ByVal g As Graphics, _
ByVal Bounds As Rectangle, _
ByVal Text As String, _
ByVal AlignToRight As Boolean)

Dim BackBrush As Brush = New
SolidBrush(Me.DataGridTableStyle.BackColor)
Dim ForeBrush As Brush = New
SolidBrush(Me.DataGridTableStyle.ForeColor)
PaintText(g, Bounds, Text, BackBrush, ForeBrush, AlignToRight)
End Sub

Protected Overridable Sub PaintText(ByVal g As Graphics, _
ByVal TextBounds As Rectangle, _
ByVal Text As String, _
ByVal BackBrush As Brush, _
ByVal ForeBrush As Brush, _
ByVal AlignToRight As Boolean)

Dim Rect As Rectangle = TextBounds
Dim RectF As RectangleF = RectF.op_Implicit(Rect) ' Convert
to RectangleF
Dim Format As StringFormat = New StringFormat

If AlignToRight Then
Format.FormatFlags =
StringFormatFlags.DirectionRightToLeft
End If

Select Case Me.Alignment
Case Is = HorizontalAlignment.Left
Format.Alignment = StringAlignment.Near
Case Is = HorizontalAlignment.Right
Format.Alignment = StringAlignment.Far
Case Is = HorizontalAlignment.Center
Format.Alignment = StringAlignment.Center
End Select

Format.FormatFlags = Format.FormatFlags Or
StringFormatFlags.NoWrap
g.FillRectangle(Brush:=BackBrush, Rect:=Rect)

Rect.Offset(0, myMargin)
Rect.Height -= myMargin
g.DrawString(Text, Me.DataGridTableStyle.DataGrid.Font,
ForeBrush, RectF, Format)
Format.Dispose()

End Sub

#End Region

#End Region

#Region "Public"

#Region "Events"

Public Event ColumnDirty(ByVal sender As Object, ByVal e As
System.EventArgs)
Public Event ColumnEdited(ByVal sender As Object, ByVal e As
System.EventArgs)

#End Region

#End Region

End Class

#End Region


Public Class ExtendedDataGridTextBoxColumn
Inherits ExtendedDataGridColumnStyle


#Region "Private"

#Region "Properties"

Private WithEvents tb As TextBox

#End Region

#Region "Subroutines"

Private Sub HideTextBox()
If tb.Focused Then
Me.DataGridTableStyle.DataGrid.Focus()
End If
tb.Visible = False
End Sub

Private Sub tb_TextChanged(ByVal sender As Object, ByVal e
As System.EventArgs) Handles tb.TextChanged
MyBase.ColumnStartedEditing(tb)
End Sub

#End Region

#End Region

#Region "Protected"

#Region "Functions"

Protected Overrides Function Commit(ByVal dataSource As
System.Windows.Forms.CurrencyManager, _
ByVal rowNum As Integer)
As Boolean

HideTextBox()
RowNumber = rowNum
GridDataSource = dataSource
If Not InEdit Then
Return True
End If

Try
Dim Value As Object = tb.Text
If NullText.Equals(Value) Then
Value = Convert.DBNull
End If
If Not Value = GetColumnTextAtRow(dataSource,
rowNum) Then
IsDirty = True
SetColumnValueAtRow(dataSource, rowNum, Value)
End If
Catch e As Exception
RollBack()
Return False
End Try
EndEdit()
Return True

End Function

Protected Overrides Function GetColumnTextAtRow(ByVal Source
As System.Windows.Forms.CurrencyManager, ByVal RowNum As Integer) As Object
Dim value As Object = Me.GetColumnValueAtRow(Source,
RowNum)

If value Is System.DBNull.Value Then
Return NullText
Else
Return value
End If

End Function

Protected Overrides Function GetMinimumHeight() As Integer

'Set the minimum height to the height of the combobox

Return tb.PreferredHeight + yMargin

End Function

#End Region

#Region "Subroutines"

Protected Overloads Overrides Sub Abort(ByVal RowNum As
Integer)
MyBase.Abort(RowNum)
HideTextBox()
End Sub

Protected Overloads Overrides Sub ConcedeFocus()
tb.Visible = False
End Sub

Protected Overloads Overrides Sub Edit(ByVal source As
System.Windows.Forms.CurrencyManager, _
ByVal rowNum As
Integer, _
ByVal bounds As
System.Drawing.Rectangle, _
ByVal [readOnly] As
Boolean, _
ByVal instantText As
String, _
ByVal cellIsVisible
As Boolean)

GridDataSource = source
RowNumber = rowNum
tb.Text = String.Empty

Dim OriginalBounds As Rectangle = bounds
Dim txt As String

OldValue = GetColumnValueAtRow(source, rowNum)

If cellIsVisible Then
bounds.Offset(xMargin, yMargin)
bounds.Width -= xMargin * 2
bounds.Height -= yMargin
tb.Bounds = bounds
tb.Visible = True
Else
tb.Bounds = OriginalBounds
tb.Visible = False
End If

If Not instantText Is Nothing Then
tb.Text = instantText
Else
tb.Text = oldvalue
End If

tb.RightToLeft =
Me.DataGridTableStyle.DataGrid.RightToLeft
tb.Focus()

If instantText Is Nothing Then
tb.SelectAll()
Else
Dim [End] As Integer = tb.Text.Length
tb.Select([End], 0)
End If

If tb.Visible Then
DataGridTableStyle.DataGrid.Invalidate(OriginalBounds)
End If

InEdit = True
End Sub

#End Region

#End Region

#Region "Public"

#Region "Properties"

Public ReadOnly Property TextBox() As TextBox
Get
Return tb
End Get
End Property

#End Region

#Region "Subroutines"

Public Sub New()
tb = New TextBox
tb.Visible = False
CellControl = CType(tb, Control)
End Sub

#End Region

#End Region

End Class



Pat
 
K

kevin

Pat,

I've also done some *Extremely* heavy modification to the standard
DataGrid object, and assocated column objects. Unlike you - I had to
provide the ability to select multiple rows and provide multiple
formats to those rows (ie Color selection and the like) based on user
preferences at the time of selection. I also needed to maintain Column
Selection, and specific Cell selection and highlighting.

For those reasons, and for a similar question you are requesting - I
handled all of the user interaction and selection on my own - without
the use (or betrayal) of the isSelected Property of elements.

This can be easily handled by simply overriding the MouseDown event on
the DataGrid object - and testing to determine what type of object you
are currently attempting to select - like so:

Private Sub MyGrid_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
Try
Dim x As HitTestInfo = Me.HitTest(New Point(e.X, e.Y))
Dim ts As DataGridColumnStyle =
Me.m_BaseStyle.GridColumnStyles.Item(x.Column)

Select Case x.Type
Case DataGrid.HitTestType.Cell
Case DataGrid.HitTestType.ColumnHeader
Case DataGrid.HitTestType.RowHeader
Case DataGrid.HitTestType.Caption
Case DataGrid.HitTestType.ColumnResize
Case DataGrid.HitTestType.None
Case DataGrid.HitTestType.ParentRows
Case DataGrid.HitTestType.RowResize
End Select
End Sub

Within the handling of these feature (i only used the cell, and column
header cases) you can track the setting and removing of highlights.
This was handled in my Grid by keeping a sorted list object that had a
key index for the rows as its key - and a highlight color object (with
a row,column integer). The value of the row and column identifies what
is highlighted. For example -1,2 means column 2 is highlighted. 2,-1
means row 2 is highlighted, and 2,2 means cell 2,2 is highlighted.

Painting of the columns is then handled in an overload of the Paint
method in a custom DataGridTextBoxColumn. When the cells are painted,
each cell checks for a custom highlight (from the arraylist) by
requesting that from the parent grid.

Its alot to take in - but with your development status to this point -
you should be able to gather what I have stated. If you dont need to go
to that depth, you can handle the RowHeader clicked case in the
mousedown stub i provided. That should get you moving along.

Kevin M. Schreiner
VP Software Architecture
Business Intelligence Force (bi4ce)
 
G

Guest

Sorry that I've now spawned an extra thread.
When the row header is clicked no particular cell is selected in the row.

When a cell is clicked, you now have an active CurrentCell in the DataGrid.

Yes, I understand. I AM talking about entire rows. I click the rowheader
and only one cell (the one belonging to the vanilla DataGridColumnStyle
column) in the selected row gets highlighted.
 
G

Guest

Hmm. Interesting. Yes I am sort of starting to come to the conclusion that
I am going to have to capture the MouseUp event (Why did you use MouseDown?).
But it irks me that I have to do that, it seems ... a little icky (what
about Keyboard selection, etc.). It seems that it would be preferable to be
able to rely on a RowSelected event. But I guess that doesn't exist.

Thanks,
 
M

Mike McIntyre

The only way I can debug what is causing the problem in your code is to see
your code ;-)
 
K

kevin

Yes, Keyboard and Mouse selection would have to be tracked equally,
although it does tend to be a bit icky, it truly depends on what the
overall goal is. In my case - as I stated before I needed to allow the
user to highlight rows,columns and cells in different colors. Also I
needed the binding source to be able to do the same thing as the data
comes from the datasource (that was pretty simple also since I was
already using the Highlighting arraylist), but it does tend to be a
little more work than anticipated.

The tracking of the MouseDown was necessary because I had to also
provide different context menu's based on the cursor position.
Different Column had can have different capabilities, as well as
overall context menu's for column and row headers. Plus I needed to
capture key combinations with the mouse click (Ctrl-Shift-Left Click
and the like).

Kevin M. Schreiner
VP Software Architecture
Business Intelligence Force (bi4ce)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top