Synchronising two datagrids together.

A

anon

I want to obtain the appearance of frozen columns as in an excel sheet so
thet the first two columns of the table remain in view at all times.

Example - I have a table of products - listed by Item and Line.
I want the Item and Line columns to remain visible whilst being able to
scroll the rest of the grid across to reveal other columns.
Ialso need the information to be present and synchronised with the rows
when I scroll down.


All the data is held in one data table - and all I need to do is freeze the
first two columns of the datagrid, but I am informed that the datagrid does
not have this functionality.

Someone came up with the ides of having two datagrids - side by side - and
showing the first two columns (of the data table) in the first grid, with
the rest of the columns ( in the data table) being shown in the second grid.
This idea would be fine, but I cannot get the two grids to scroll down
together as if bound together.

Can anyone please come up with a means of facilitating this idea - or
suggest another means.

I am using VB.NET 2002 Standard edition.

Thanks in advance

Terry
 
K

Ken Tucker [MVP]

Hi,

Here is a column style that locks a column on the datagrid. It is
still under development. basically if you set the locked property to true
it places a panel over the datagrid that has the locked column drawn on it.
Currently it only will lock the first column.

Imports System.Drawing.Drawing2D

Public Class ColoredGridColumn

Inherits DataGridTextBoxColumn

Dim mForeColor As Color = Color.Black

Dim mBackColor As Color = Color.White

Dim WithEvents mctrl As DblBufferPanel

Dim WithEvents dg As DataGrid

Dim pt As New Point

Dim bPanelOnly As Boolean = False

Dim cm As CurrencyManager

Dim ar As Boolean

Private Class DblBufferPanel

Inherits Panel

Public Sub New()

Me.SetStyle(ControlStyles.DoubleBuffer, True)

End Sub

End Class

Public Property ForeColor() As Color

Get

Return mForeColor

End Get

Set(ByVal Value As Color)

mForeColor = Value

End Set

End Property

Public Property BackColor() As Color

Get

Return mBackColor

End Get

Set(ByVal Value As Color)

mBackColor = Value

End Set

End Property

Public Property Locked() As Boolean

Get

Return Not mctrl Is Nothing

End Get

Set(ByVal Value As Boolean)

If Value = False Then

mctrl = Nothing

Else

mctrl = New DblBufferPanel

End If

End Set

End Property

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
backBrush As System.Drawing.Brush, ByVal foreBrush As System.Drawing.Brush,
ByVal alignToRight As Boolean)

Dim brFore As Brush

Dim brBack As Brush

Dim cFore As Color

Dim cBack As Color

Static bPainted As Boolean = False

If Not bPainted And Not (mctrl Is Nothing) Then

dg = Me.DataGridTableStyle.DataGrid

dg.Parent.Controls.Add(mctrl)

MovePanel()

mctrl.BringToFront()

pt = dg.GetCellBounds(0, 0).Location

If TypeOf dg.DataSource Is DataTable Then

AddHandler DirectCast(dg.DataSource, DataTable).DefaultView.ListChanged,
AddressOf dv_ListChanged

ElseIf TypeOf dg.DataSource Is DataView Then

AddHandler DirectCast(dg.DataSource, DataView).ListChanged, AddressOf
dv_ListChanged

End If

End If

cm = source

ar = alignToRight

bPainted = True

If Me.DataGridTableStyle.DataGrid.IsSelected(rowNum) Then

cFore = Me.DataGridTableStyle.SelectionForeColor

cBack = Me.DataGridTableStyle.SelectionBackColor

Else

cFore = ForeColor

cBack = BackColor

End If

brBack = New LinearGradientBrush(bounds, cBack, Color.White, 90, False)

brFore = New SolidBrush(cFore)



Dim bl As New Blend

bl.Factors = New Single() {0.0F, 0.1F, 0.5F, 0.7F, 0.7F, 0.5F, 0.3F, 0.2F,
0}

bl.Positions = New Single() {0, 0.1F, 0.2F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F,
1.0F}

DirectCast(brBack, LinearGradientBrush).Blend = bl

If Not bPanelOnly Then

MyBase.Paint(g, bounds, source, rowNum, brBack, brFore, alignToRight)

End If

If Not (mctrl Is Nothing) Then

' if there is another control to draw on move the bounds to the right edge
if htere is not then it will ignore that directive right? yesgot oitf the
control

'mctrl.BackgroundImage = bm

PaintRow(mctrl.CreateGraphics, bounds, rowNum)

End If

If Me.GetColumnValueAtRow(source, rowNum).ToString = "Davolio" Then

Me.DataGridTableStyle.DataGrid.Select(rowNum)

End If

End Sub

Public Shadows Sub BeginUpdate()

MyBase.BeginUpdate()

End Sub

Public Shadows Sub EndUpdate()

MyBase.EndUpdate()

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)

MyBase.Edit(source, rowNum, bounds, [readOnly], instantText, cellIsVisible)

MyBase.TextBox.ForeColor = ForeColor()

MyBase.TextBox.BackColor = BackColor

End Sub

Public Sub New()

End Sub

Private Sub dg_Scroll(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Scroll

Trace.WriteLine("Scroll")

RedrawPanel()

End Sub

Private Sub MovePanel()

mctrl.Location = New Point(dg.Left + dg.RowHeaderWidth + 2, dg.Top + 21)

Dim intFactor As Integer = SystemInformation.HorizontalScrollBarHeight

For Each ctrl As Control In Me.DataGridTableStyle.DataGrid.Controls

If TypeOf ctrl Is HScrollBar Then

If Not DirectCast(ctrl, HScrollBar).Visible Then intFactor = 0

End If

Next

mctrl.Height = dg.Height - 21 - intFactor - 2

End Sub

Private Sub dg_Move(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Move

MovePanel()

End Sub

Private Sub PreparePanel()

Dim sf As New StringFormat

sf.LineAlignment = StringAlignment.Center

Dim rDraw As New RectangleF(0, 0, Me.Width, 20)

Dim g As Graphics = mctrl.CreateGraphics

g.Clear(mctrl.BackColor)

Try

g.DrawString(Me.HeaderText, dg.Font, Brushes.Black, rDraw, sf)

ControlPaint.DrawBorder3D(g, _

New Rectangle(0, 0, Me.Width, 19), Border3DStyle.RaisedInner)

Catch

End Try

End Sub

Private Sub dv_ListChanged(ByVal sender As Object, ByVal e As
System.ComponentModel.ListChangedEventArgs)

'PreparePanel()

RedrawPanel()

End Sub

Private Sub dg_VisibleChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles dg.VisibleChanged

Try

mctrl.Visible = dg.Visible

Catch ex As Exception

End Try

End Sub

Private Sub dg_SizeChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles dg.SizeChanged

MovePanel()

RedrawPanel()

End Sub

Private Sub RedrawPanel()

Static oldTop As Integer = 0

If Not mctrl Is Nothing And dg.VisibleRowCount > 0 Then

Application.DoEvents()

Dim hti As DataGrid.HitTestInfo = dg.HitTest(pt)

Dim newRow As Integer = hti.Row

Trace.WriteLine(String.Format("First Row {0} Visible rows {1}", hti.Row,
dg.VisibleRowCount))

oldTop = newRow

bPanelOnly = True

For x As Integer = oldTop To oldTop + dg.VisibleRowCount - 1

If x < cm.Count Then

Trace.WriteLine(String.Format("Drawing Row {0} {1}", x, dg.GetCellBounds(x,
dg.FirstVisibleColumn)))

Paint(Nothing, dg.GetCellBounds(x, dg.FirstVisibleColumn), _

cm, x, Nothing, Nothing, ar)

End If

Next

bPanelOnly = False

End If

End Sub

Private Sub dg_Resize(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Resize

MovePanel()

End Sub

Private Sub mctrl_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles mctrl.Paint

Dim sf As New StringFormat

sf.LineAlignment = StringAlignment.Center

Dim rDraw As New RectangleF(0, 0, Me.Width, 20)

Dim g As Graphics = e.Graphics

g.Clear(mctrl.BackColor)

Try

g.DrawString(Me.HeaderText, dg.Font, Brushes.Black, rDraw, sf)

ControlPaint.DrawBorder3D(g, _

New Rectangle(0, 0, Me.Width, 19), Border3DStyle.RaisedInner)

Debug.WriteLine("Panel Paint")

Dim hti As DataGrid.HitTestInfo = dg.HitTest(pt)

Dim newRow As Integer = hti.Row

Dim oldTop As Integer

Trace.WriteLine(String.Format("First Row {0} Visible rows {1}", hti.Row,
dg.VisibleRowCount))

oldTop = newRow

bPanelOnly = True

For x As Integer = oldTop To oldTop + dg.VisibleRowCount - 1

If x < cm.Count Then

Trace.WriteLine(String.Format("Drawing Row {0} {1}", x, dg.GetCellBounds(x,
dg.FirstVisibleColumn)))

Paint(Nothing, dg.GetCellBounds(x, dg.FirstVisibleColumn), _

cm, x, Nothing, Nothing, ar)

End If

Next

Catch

End Try

End Sub

Private Sub PaintRow(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal
rownum As Integer)

Dim brFore As Brush

Dim brBack As Brush

Dim cFore As Color

Dim cBack As Color

Dim bounds2 As New Rectangle(0, bounds.Y - 21, bounds.Width, bounds.Height)

If Me.DataGridTableStyle.DataGrid.IsSelected(rownum) Then

cFore = Me.DataGridTableStyle.SelectionForeColor

cBack = Me.DataGridTableStyle.SelectionBackColor

Else

cFore = ForeColor

cBack = BackColor

End If

brBack = New LinearGradientBrush(bounds, cBack, Color.White, 90, False)

brFore = New SolidBrush(cFore)



Dim bl As New Blend

bl.Factors = New Single() {0.0F, 0.1F, 0.5F, 0.7F, 0.7F, 0.5F, 0.3F, 0.2F,
0}

bl.Positions = New Single() {0, 0.1F, 0.2F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F,
1.0F}

DirectCast(brBack, LinearGradientBrush).Blend = bl

If mctrl.Width <> Me.Width Then mctrl.Width = Me.Width

MyBase.Paint(g, bounds2, cm, rownum, _

brBack, brFore, ar)

If rownum = cm.Count - 1 Then

Dim br As New SolidBrush(Me.DataGridTableStyle.DataGrid.BackgroundColor)

g.FillRectangle(br, 0, bounds2.Bottom + 1, mctrl.Width, _

mctrl.Height - bounds2.Bottom - 1)

End If

End Sub

End Class



Ken

-------------------------------


I want to obtain the appearance of frozen columns as in an excel sheet so
thet the first two columns of the table remain in view at all times.

Example - I have a table of products - listed by Item and Line.
I want the Item and Line columns to remain visible whilst being able to
scroll the rest of the grid across to reveal other columns.
Ialso need the information to be present and synchronised with the rows
when I scroll down.


All the data is held in one data table - and all I need to do is freeze the
first two columns of the datagrid, but I am informed that the datagrid does
not have this functionality.

Someone came up with the ides of having two datagrids - side by side - and
showing the first two columns (of the data table) in the first grid, with
the rest of the columns ( in the data table) being shown in the second grid.
This idea would be fine, but I cannot get the two grids to scroll down
together as if bound together.

Can anyone please come up with a means of facilitating this idea - or
suggest another means.

I am using VB.NET 2002 Standard edition.

Thanks in advance

Terry
 
A

anon

thanks for that feedback - it's a starting point.
Terry




Ken Tucker said:
Hi,

Here is a column style that locks a column on the datagrid. It is
still under development. basically if you set the locked property to true
it places a panel over the datagrid that has the locked column drawn on it.
Currently it only will lock the first column.

Imports System.Drawing.Drawing2D

Public Class ColoredGridColumn

Inherits DataGridTextBoxColumn

Dim mForeColor As Color = Color.Black

Dim mBackColor As Color = Color.White

Dim WithEvents mctrl As DblBufferPanel

Dim WithEvents dg As DataGrid

Dim pt As New Point

Dim bPanelOnly As Boolean = False

Dim cm As CurrencyManager

Dim ar As Boolean

Private Class DblBufferPanel

Inherits Panel

Public Sub New()

Me.SetStyle(ControlStyles.DoubleBuffer, True)

End Sub

End Class

Public Property ForeColor() As Color

Get

Return mForeColor

End Get

Set(ByVal Value As Color)

mForeColor = Value

End Set

End Property

Public Property BackColor() As Color

Get

Return mBackColor

End Get

Set(ByVal Value As Color)

mBackColor = Value

End Set

End Property

Public Property Locked() As Boolean

Get

Return Not mctrl Is Nothing

End Get

Set(ByVal Value As Boolean)

If Value = False Then

mctrl = Nothing

Else

mctrl = New DblBufferPanel

End If

End Set

End Property

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
backBrush As System.Drawing.Brush, ByVal foreBrush As System.Drawing.Brush,
ByVal alignToRight As Boolean)

Dim brFore As Brush

Dim brBack As Brush

Dim cFore As Color

Dim cBack As Color

Static bPainted As Boolean = False

If Not bPainted And Not (mctrl Is Nothing) Then

dg = Me.DataGridTableStyle.DataGrid

dg.Parent.Controls.Add(mctrl)

MovePanel()

mctrl.BringToFront()

pt = dg.GetCellBounds(0, 0).Location

If TypeOf dg.DataSource Is DataTable Then

AddHandler DirectCast(dg.DataSource, DataTable).DefaultView.ListChanged,
AddressOf dv_ListChanged

ElseIf TypeOf dg.DataSource Is DataView Then

AddHandler DirectCast(dg.DataSource, DataView).ListChanged, AddressOf
dv_ListChanged

End If

End If

cm = source

ar = alignToRight

bPainted = True

If Me.DataGridTableStyle.DataGrid.IsSelected(rowNum) Then

cFore = Me.DataGridTableStyle.SelectionForeColor

cBack = Me.DataGridTableStyle.SelectionBackColor

Else

cFore = ForeColor

cBack = BackColor

End If

brBack = New LinearGradientBrush(bounds, cBack, Color.White, 90, False)

brFore = New SolidBrush(cFore)



Dim bl As New Blend

bl.Factors = New Single() {0.0F, 0.1F, 0.5F, 0.7F, 0.7F, 0.5F, 0.3F, 0.2F,
0}

bl.Positions = New Single() {0, 0.1F, 0.2F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F,
1.0F}

DirectCast(brBack, LinearGradientBrush).Blend = bl

If Not bPanelOnly Then

MyBase.Paint(g, bounds, source, rowNum, brBack, brFore, alignToRight)

End If

If Not (mctrl Is Nothing) Then

' if there is another control to draw on move the bounds to the right edge
if htere is not then it will ignore that directive right? yesgot oitf the
control

'mctrl.BackgroundImage = bm

PaintRow(mctrl.CreateGraphics, bounds, rowNum)

End If

If Me.GetColumnValueAtRow(source, rowNum).ToString = "Davolio" Then

Me.DataGridTableStyle.DataGrid.Select(rowNum)

End If

End Sub

Public Shadows Sub BeginUpdate()

MyBase.BeginUpdate()

End Sub

Public Shadows Sub EndUpdate()

MyBase.EndUpdate()

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)

MyBase.Edit(source, rowNum, bounds, [readOnly], instantText, cellIsVisible)

MyBase.TextBox.ForeColor = ForeColor()

MyBase.TextBox.BackColor = BackColor

End Sub

Public Sub New()

End Sub

Private Sub dg_Scroll(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Scroll

Trace.WriteLine("Scroll")

RedrawPanel()

End Sub

Private Sub MovePanel()

mctrl.Location = New Point(dg.Left + dg.RowHeaderWidth + 2, dg.Top + 21)

Dim intFactor As Integer = SystemInformation.HorizontalScrollBarHeight

For Each ctrl As Control In Me.DataGridTableStyle.DataGrid.Controls

If TypeOf ctrl Is HScrollBar Then

If Not DirectCast(ctrl, HScrollBar).Visible Then intFactor = 0

End If

Next

mctrl.Height = dg.Height - 21 - intFactor - 2

End Sub

Private Sub dg_Move(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Move

MovePanel()

End Sub

Private Sub PreparePanel()

Dim sf As New StringFormat

sf.LineAlignment = StringAlignment.Center

Dim rDraw As New RectangleF(0, 0, Me.Width, 20)

Dim g As Graphics = mctrl.CreateGraphics

g.Clear(mctrl.BackColor)

Try

g.DrawString(Me.HeaderText, dg.Font, Brushes.Black, rDraw, sf)

ControlPaint.DrawBorder3D(g, _

New Rectangle(0, 0, Me.Width, 19), Border3DStyle.RaisedInner)

Catch

End Try

End Sub

Private Sub dv_ListChanged(ByVal sender As Object, ByVal e As
System.ComponentModel.ListChangedEventArgs)

'PreparePanel()

RedrawPanel()

End Sub

Private Sub dg_VisibleChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles dg.VisibleChanged

Try

mctrl.Visible = dg.Visible

Catch ex As Exception

End Try

End Sub

Private Sub dg_SizeChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles dg.SizeChanged

MovePanel()

RedrawPanel()

End Sub

Private Sub RedrawPanel()

Static oldTop As Integer = 0

If Not mctrl Is Nothing And dg.VisibleRowCount > 0 Then

Application.DoEvents()

Dim hti As DataGrid.HitTestInfo = dg.HitTest(pt)

Dim newRow As Integer = hti.Row

Trace.WriteLine(String.Format("First Row {0} Visible rows {1}", hti.Row,
dg.VisibleRowCount))

oldTop = newRow

bPanelOnly = True

For x As Integer = oldTop To oldTop + dg.VisibleRowCount - 1

If x < cm.Count Then

Trace.WriteLine(String.Format("Drawing Row {0} {1}", x, dg.GetCellBounds(x,
dg.FirstVisibleColumn)))

Paint(Nothing, dg.GetCellBounds(x, dg.FirstVisibleColumn), _

cm, x, Nothing, Nothing, ar)

End If

Next

bPanelOnly = False

End If

End Sub

Private Sub dg_Resize(ByVal sender As Object, ByVal e As System.EventArgs)
Handles dg.Resize

MovePanel()

End Sub

Private Sub mctrl_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles mctrl.Paint

Dim sf As New StringFormat

sf.LineAlignment = StringAlignment.Center

Dim rDraw As New RectangleF(0, 0, Me.Width, 20)

Dim g As Graphics = e.Graphics

g.Clear(mctrl.BackColor)

Try

g.DrawString(Me.HeaderText, dg.Font, Brushes.Black, rDraw, sf)

ControlPaint.DrawBorder3D(g, _

New Rectangle(0, 0, Me.Width, 19), Border3DStyle.RaisedInner)

Debug.WriteLine("Panel Paint")

Dim hti As DataGrid.HitTestInfo = dg.HitTest(pt)

Dim newRow As Integer = hti.Row

Dim oldTop As Integer

Trace.WriteLine(String.Format("First Row {0} Visible rows {1}", hti.Row,
dg.VisibleRowCount))

oldTop = newRow

bPanelOnly = True

For x As Integer = oldTop To oldTop + dg.VisibleRowCount - 1

If x < cm.Count Then

Trace.WriteLine(String.Format("Drawing Row {0} {1}", x, dg.GetCellBounds(x,
dg.FirstVisibleColumn)))

Paint(Nothing, dg.GetCellBounds(x, dg.FirstVisibleColumn), _

cm, x, Nothing, Nothing, ar)

End If

Next

Catch

End Try

End Sub

Private Sub PaintRow(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal
rownum As Integer)

Dim brFore As Brush

Dim brBack As Brush

Dim cFore As Color

Dim cBack As Color

Dim bounds2 As New Rectangle(0, bounds.Y - 21, bounds.Width, bounds.Height)

If Me.DataGridTableStyle.DataGrid.IsSelected(rownum) Then

cFore = Me.DataGridTableStyle.SelectionForeColor

cBack = Me.DataGridTableStyle.SelectionBackColor

Else

cFore = ForeColor

cBack = BackColor

End If

brBack = New LinearGradientBrush(bounds, cBack, Color.White, 90, False)

brFore = New SolidBrush(cFore)



Dim bl As New Blend

bl.Factors = New Single() {0.0F, 0.1F, 0.5F, 0.7F, 0.7F, 0.5F, 0.3F, 0.2F,
0}

bl.Positions = New Single() {0, 0.1F, 0.2F, 0.5F, 0.6F, 0.7F, 0.8F, 0.9F,
1.0F}

DirectCast(brBack, LinearGradientBrush).Blend = bl

If mctrl.Width <> Me.Width Then mctrl.Width = Me.Width

MyBase.Paint(g, bounds2, cm, rownum, _

brBack, brFore, ar)

If rownum = cm.Count - 1 Then

Dim br As New SolidBrush(Me.DataGridTableStyle.DataGrid.BackgroundColor)

g.FillRectangle(br, 0, bounds2.Bottom + 1, mctrl.Width, _

mctrl.Height - bounds2.Bottom - 1)

End If

End Sub

End Class



Ken

-------------------------------


I want to obtain the appearance of frozen columns as in an excel sheet so
thet the first two columns of the table remain in view at all times.

Example - I have a table of products - listed by Item and Line.
I want the Item and Line columns to remain visible whilst being able to
scroll the rest of the grid across to reveal other columns.
Ialso need the information to be present and synchronised with the rows
when I scroll down.


All the data is held in one data table - and all I need to do is freeze the
first two columns of the datagrid, but I am informed that the datagrid does
not have this functionality.

Someone came up with the ides of having two datagrids - side by side - and
showing the first two columns (of the data table) in the first grid, with
the rest of the columns ( in the data table) being shown in the second grid.
This idea would be fine, but I cannot get the two grids to scroll down
together as if bound together.

Can anyone please come up with a means of facilitating this idea - or
suggest another means.

I am using VB.NET 2002 Standard edition.

Thanks in advance

Terry
 

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