This is a VB .NET application where we are showing the output of a SQL statement in a Datagrid view. I'm using .NET 2005.
We need to get the separators of the headers on the grid control to be the same colors as the GridColor on the form.
We've tried looking through all of the properties of the DataGridView control, and found some interesting things that looked promising such as the DataGridViewAdvancedHeaderStyle, and DataGridViewHeaderBorderStyle, but none of it seems to allow you to change the colors on it.
Does anyone know how to do this without remaking the entire thing with a GDI+ control?
Well, I never did find a property for this, so I ended up creating a custom component, and overloading the OnPaint event handler to draw a line over the existing one.
Here is the code for it if anyone else ever comes across this post looking for a solution:
Private Sub CustomDataGridView_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
Dim pen As New Pen(Me.GridColor)
Dim TWidth As Integer = 2
Dim HeaderWidth As Integer = 0
If Me.RowHeadersVisible Then
HeaderWidth = Me.RowHeadersWidth
End If
For Each column As DataGridViewColumn In Me.Columns
Dim x As Integer = HeaderWidth + TWidth - 1
TWidth += column.Width
Dim top As Integer = column.HeaderCell.ContentBounds.Top
Dim bottom As Integer = column.HeaderCell.ContentBounds.Bottom + 1
pen.Width = 2
g.DrawLine(pen, x, top, x, bottom)
Next column
End Sub
To change the backcolor of the Column Headers in a datagridview, choose False for EnableHeadersVisualStyles. Then open ColumnHeadersDefaultCellStyle and choose the background color.
I can't see the picture but what about playing with these?
DataGridView.ColumnBordersHeaderStyle
DataGridView.RowBordersHeaderStyle
Related
I've updated the code and now it says that index was outside of the bounds of the array and also I'm in cs1 in high school, and accidentally made this account using my school google account.
I have included the code specific to moving the bricks down (by just a little), the code for the array, and the code for the entire timer block which as you can see is very organized with regions
Dim newbrickx, newbricky As Integer
newbrickx = bricks(i).Location.X
newbricky = bricks(i).Location.Y + 10
'left wall
If (lblball.Location.X >= Me.ClientSize.Width - lblball.Width) Then
bricks(i).Location = New Point(newbrickx, newbricky)
End If
'right wall
If (lblball.Location.X = 0) Then
bricks(i).Location = New Point(newbrickx, newbricky)
End If
the array (in the form load event)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bricks = New PictureBox() {PictureBox1, PictureBox2, PictureBox3, PictureBox4, PictureBox5, PictureBox6, PictureBox7, PictureBox8}
End Sub
I don't have the reputation to write comments yet, so I do it this way.
I assume bricks is an array containing your pictureboxes. So, inside the conditional block you can assign the new position like this:
bricks(i).Location = New Point(newbrickx, newbricky)
And as commented before you should probably also check for the y coordinates to make sure it is not out of bounds.
why can I run the following code faultless under a button even but not under the form load even?
For Each line As String In System.IO.File.ReadAllLines("c:\pos.xml")
If line.Contains("<POS>") = True Then
Dim tagless As String = StripTags(line)
Dim parts As String() = tagless.Split(New Char() {","})
Dim XVAL As Decimal = parts(0)
Dim YVAL As Decimal = parts(1)
'paint markers...
Dim myBrush As New SolidBrush(Color.FromArgb(128, Color.Red))
Dim formGraphics As System.Drawing.Graphics
formGraphics = Me.PictureBox1.CreateGraphics()
formGraphics.FillEllipse(myBrush, New Rectangle(XVAL - 35, YVAL - 35, 70, 70))
myBrush.Dispose()
formGraphics.Dispose()
End If
Next
Here is the striptag function if requierd...
Function StripTags(ByVal html As String) As String
' Remove HTML tags.
Return Regex.Replace(html, "<.*?>", "")
End Function
The right way to draw is hardly ever with CreateGraphics. This will draw something that does not persist. When the area is invalidated such as the form is minimized or another form/app is dragged over it, your shapes will disappear.
You should also turn on Option Strict. There are numerous type errors in the code. For instance there is no Rectangle constructor which takes a Decimal. That is not even the right Class for non integers, but RectangleF doesn't take a Decimal either.
The core problem is that the form is shown at the end of the form load event. So, your code is running/drawing before the form is visible and nothing is shown. Even if the form was already showing, whatever you draw would not be retained if the user minimized the form or moved another window across it.
' form level list to store the data
Private XY As New List(Of PointF) ' pts
Then in form load event, read the data and add to the list
For Each line As String In System.IO.File.ReadAllLines("...")
If line.Contains("<POS>") = True Then
Dim tagless As String = StripTags(line)
' c required under Option Strict
Dim parts As String() = tagless.Split(New Char() {","c})
' convert values to single. create a PointF
Dim ptF As New PointF(Convert.ToSingle(parts(0)), Convert.ToSingle(parts(1)))
' add to list
XY.Add(ptF)
End If
Next
The next thing that happens should be the form being shown and the paint event invoked. The data is used in the Paint Event:
Dim rectF As RectangleF
Using myB As New SolidBrush(Color.FromArgb(128, Color.Red))
For Each ptF As PointF In XY
rectF = New RectangleF(ptF.X - 35, ptF.Y - 35,
70, 70)
e.Graphics.FillEllipse(myB, rectF)
Next
End Using
If you have other code to add Point data, like a Button click, after you add, change or remove data, use Invalidate to force a redraw: Me.Invaludate() is you are drawing to the form, or PictureBox1.Invalidate() if you are drawing over a control.
The lesson is that now, every time the form needs to be repainted, your shapes will be redrawn as well.
I am trying to add a custom message to the background of a listview when there is no data to display. I have turned on the UserPaint flag via the call below which has given me exactly what I want.
SetStyle(ControlStyles.UserPaint, True)
However, this then disables all of the Item and SubItem drawing for the entire ListView. I am only assuming that with this set to true, I am now responsible for drawing all items myself (I could be wrong).
If I am correct, does anyone have a tutorial on how to custom draw list items manually, or give me some examples as I am very new to working with messages and would love to get my head around it all.
Below is my code for drawing my custom message via the OnPaint method:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim MessageFont As Font = _EmptyMessageFont
Dim MessageColour As Color = _EmptyMessageColour
Dim MessageText As String = _EmptyMessageString
If Not Me.Items.Count > 0 Then
Dim flags As TextFormatFlags
Dim rec As New Rectangle(Me.Location.X + 2, Me.Location.Y + 2, Me.Width - 4, Me.Height - 4)
flags = TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter Or TextFormatFlags.SingleLine
TextRenderer.DrawText(e.Graphics, MessageText, MessageFont, rec, MessageColour, flags)
End If
MyBase.OnPaint(e)
End Sub
Not sure how to add code in this method to handle the normal drawing of list items.
I'd like to make a Cartesian Coordinate System in a Windows form and be able to plot (x,y) coordinates in it.
How do i do this? I already did my research but unfortunately i only land on "charts" and not the Cartesian plane.
Any links regarding my problem will help ... thanks ...
In WinForms, you can use a PictureBox control and then draw on it using primitives such as DrawLine, DrawEllipse, etc. The following SO question contains an example:
how to draw drawings in picture box
In WPF, you can use a Canvas control similarly:
WPF canvas drawing with Graphics
If you want automatic axes and labeling, Charts are indeed the way to go. For your use case, a point chart seems like the right solution:
Point Chart (Chart Controls)
You should create a custom UserControl and use the Paint even to draw on the surface of the control. The Paint event provides you with a Graphics object which you can use to draw the graph. The big thing to know, however, is that you will need to swap your Y axis. In windows, the top-left of the screen is 0,0 rather than the bottom-left.
So, for instance, the following code will draw the x and y axis of a graph on a contorl:
Public Class CartesianGraph
Public Property BottomLeftExtent() As Point
Get
Return _bottomLeftExtent
End Get
Set(ByVal value As Point)
_bottomLeftExtent = value
End Set
End Property
Private _bottomLeftExtent As Point = New Point(-100, -100)
Public Property TopRightExtent() As Point
Get
Return _topRightExtent
End Get
Set(ByVal value As Point)
_topRightExtent = value
End Set
End Property
Private _topRightExtent As Point = New Point(100, 100)
Private Sub CartesianGraph_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim extentHeight As Integer = _topRightExtent.Y - _bottomLeftExtent.Y
Dim extentWidth As Integer = _topRightExtent.X - _bottomLeftExtent.X
If (extentHeight <> 0) And (extentWidth <> 0) Then
If (_bottomLeftExtent.Y <= 0) And (_topRightExtent.Y >= 0) Then
Dim xAxis As Integer = e.ClipRectangle.Height - (_bottomLeftExtent.Y * -1 * e.ClipRectangle.Height \ extentHeight)
e.Graphics.DrawLine(New Pen(ForeColor), 0, xAxis, e.ClipRectangle.Width, xAxis)
End If
If (_bottomLeftExtent.X <= 0) And (_topRightExtent.X >= 0) Then
Dim yAxis As Integer = e.ClipRectangle.Width * _bottomLeftExtent.X * -1 \ extentWidth
e.Graphics.DrawLine(New Pen(ForeColor), yAxis, 0, yAxis, e.ClipRectangle.Height)
End If
End If
End Sub
End Class
.NET has a charting library, but there are a few open source projects that do this sort of thing quite well. If you want to plot coordinates Zedgraph makes this relatively easy and is quite flexible.
this ZedGraph Example gives a great introduction
Dynamic Data Display is also worth looking at, but it is WPF, not Windows Forms
I have several label boxes on my design form that all share the naming convention lbl_#.text where # ranges from 1 to 60. I want to make a loop that iterates through each lbl_#.text adding some incremental value, let's say multiples of 2 for this question's theoretical purpose.
Something such that the end result would amount to the following:
lbl_1.text = "2"
lbl_2.text = "4"
lbl_3.text = "6"
...
lbl_60.text = "120"
I'm not sure how to access each of these labels through the coding side, I only know how to explicitly mention each label and assign a value :/
There are a few options here.
In this situation the labels will often have a common container, such as panel or groupbox control. In that case:
Dim formLabels = myContainerControl.Controls.OfType(Of Label)()
For Each formLabel As Label In formLabels
'...
Next formLabel
Of course, this mixes logical groups with visual groupings. Those two things don't always align well, so you can also...
Add them all to a Label array (or List(Of Label) or any other enumerable):
Dim formLabels(60) As Label = {lbl_1, lbl_2, lbl_3 .... }
For Each formLabel As Label in formLabels
'...
Next formLabel
But sometimes that's more trouble than it's worth, even if you use a loop to create the collection, and so you can also
Use the .Name property (in conjunction with a naming convention to identify your desired controls):
Dim formLabels = Controls.Where(Function(c) c.Name.StartsWith("lbl_"))
For Each formLabel As Label In formLabels
'...
Next formLabel
Some combination of the above (for example, code in the form load event to create a list based on the name property).
Notice the actual For Each loop is exactly the same in all of those options. No matter what you do, get to the point where you can write a single expression to identify the label control, and then run a simple loop over the expression result.
This points to a final strategy: think in terms of binding to a data source. With a data source, your labels are created as part of a DataGridView, FlowLayoutPanel, or similar control. Then you can iterate the rows in the grid or panel.
Use the Controls collection:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
For i = 1 To 3
Dim myLabel As Label = CType(Me.Controls("lbl_" & i), Label)
myLabel.Text = ...whatever value you want to put here
Next
End Sub
End Class
If you don't know how many labels there are, one option is to use a Do Loop.
Dim lblTarget As Label = Nothing
Dim intCursor As Integer = 1
Dim bolFirstIteration As Boolean = True
Do Until lblTarget Is Nothing AndAlso Not bolFirstIteration
If bolFirstIteration Then
bolFirstIteration = False
End If
lblTarget = CType(Me.Controls("lbl_" & intCursor.ToString()), Label)
If Not lblTarget Is Nothing Then
lblTarget.Text = (intCursor * 2).ToString()
End If
intCursor += 1
Loop