Generate Panels based on dynamic location - vb.net

I have a big panel and a function which generates panels which hold two labels. Each panel is generated under the previous one, in a message-based system.
The function looks like this:
Function GenerateMessage(ByVal author As String, ByVal message As String)
Dim user_name As String = author
Dim user_id As String = message
Dim Lbl As New Panel
AddHandler Lbl.DoubleClick, AddressOf DoubleC
AddHandler Lbl.MouseDown, AddressOf MouseDownme
AddHandler Lbl.MouseEnter, AddressOf Hoverme
AddHandler Lbl.MouseLeave, AddressOf Leaveme
Lbl.Name = "messagePanel"
If lastSavedControl Is Nothing Then
Lbl.Location = New Point(My.Settings.LastMessageX, My.Settings.LastMessageY + 70)
Else
Lbl.Location = New Point(lastSavedControl.Location.X, lastSavedControl.Location.Y + 70)
End If
Lbl.Size = New System.Drawing.Size(containerReceived.Width, containerReceived.Height)
Lbl.Font = New Font("Leelawadee UI", 10, FontStyle.Regular)
Lbl.BorderStyle = BorderStyle.None
Lbl.Cursor = Cursors.Hand
Lbl.Visible = True
Lbl.BackColor = Color.Transparent
Lbl.ForeColor = Color.Silver
Lbl.Anchor = AnchorStyles.Left + AnchorStyles.Top
chatPanel.Controls.Add(Lbl)
Debug.WriteLine(Lbl.Location.X)
Debug.WriteLine(Lbl.Location.Y)
My.Settings.LastMessageX = Lbl.Location.X
My.Settings.LastMessageY = Lbl.Location.Y
Dim newAuthor As New Label
AddHandler newAuthor.DoubleClick, AddressOf LabelDoubleC
AddHandler newAuthor.MouseDown, AddressOf LabelMouseDown
AddHandler newAuthor.MouseEnter, AddressOf LabelHover
AddHandler newAuthor.MouseLeave, AddressOf LabelMouseLeave
newAuthor.Name = "authorLbl"
newAuthor.AutoSize = True
newAuthor.Location = New Point(messageReceived.Location.X, 6) ' 9, 5
newAuthor.Font = New Font("Leelawadee UI", 12, FontStyle.Regular)
newAuthor.Cursor = Cursors.Hand
newAuthor.Anchor = AnchorStyles.Top + AnchorStyles.Left
newAuthor.Visible = True
newAuthor.Text = $"#{user_name}"
newAuthor.Tag = "userLbl"
newAuthor.BackColor = Color.Transparent
newAuthor.ForeColor = Color.Black
Lbl.Controls.Add(newAuthor)
newAuthor.Parent = Lbl
Dim newMessage As New Label
AddHandler newMessage.DoubleClick, AddressOf LabelDoubleC
AddHandler newMessage.MouseDown, AddressOf LabelMouseDown
AddHandler newMessage.MouseEnter, AddressOf LabelHover
AddHandler newMessage.MouseLeave, AddressOf LabelMouseLeave
newMessage.Name = "messageLbl"
newMessage.Location = New Point(messageReceived.Location.X, 35) ' 9, 5
For Each lineofText In chatBox.Lines
newMessage.Size = New Point(messageReceived.Width, 12 * lineofText)
Next
newMessage.Font = New Font("Leelawadee UI", 9, FontStyle.Regular)
newMessage.Cursor = Cursors.Hand
newAuthor.Anchor = AnchorStyles.Top + AnchorStyles.Left
newMessage.Visible = True
newMessage.Text = user_id
newMessage.Tag = "idLbl"
newMessage.BackColor = Color.Transparent
newMessage.ForeColor = Color.Black
Lbl.Controls.Add(newMessage)
newMessage.Parent = Lbl
End Function
My.Settings.LastMessageX and My.Settings.LastMessageY are two integers defined which are saved on each panel generation. lastSavedControl is also a variable, defined at run-time, which holds the entire control. But using the Y location from lastSavedControl doesn't seem to make a difference.
I also have AutoScroll enabled on the big panel to help with scrolling when there are too many messages to fit.
When the scrollbar is enabled and scrolled all the way down, the Y (height) property on the generated panel seems to skip. It doubles every time I generate a new panel. I suspect it's because the Y value of the big panel changes when the scrollbar is used. I don't know how to get around this.
Example image of panels skipping
I have unsuccessfully tried to save the location of the previously generated panel, and the bug seems to bypass that, likely because of the Y value of the big panel changing when scrolling.

Related

Datagridview image column moves on update

On my form i got datagridview which is populating by some data. There is also a combobx on this form and when selection change has happend then based on selected value - datagridview datasource is set to be nothing and then filled up again. Everything was working well until i decided to add additional datagridview image column which i placed on the end of grid and to show images based on text from some previous column text data. Unfortunetly i have problem with displaying images itself (red marks) and biggest problem is when i change combobox selection my additional datagrid image column is moving 1 position to left everytime combo is changed. I've spent whole day looking for issue but can't find any answer on that. I thought maybe that's because grid datasource is not being cleaned (nothing) but it is. Can you please help me out what could be a problem here? Below find my code and screenshoots of problem. Hope you help me to fix it as i am really stackoverflowed.
My Form Load event - that's where i load combobox:
Private Sub FrmTransportView_Load(sender As Object, e As EventArgs) Handles MyBase.Load
RemoveHandler CboProjects.SelectedIndexChanged, AddressOf CboProjects_SelectedIndexChanged
Try
mydb.OpenConn()
If mydb.conn.State = ConnectionState.Open Then
Form.InitCombo(CboProjects, "SELECT * from tbProjekt", mydb.conn, "Nazwa", "Id")
mydb.CloseConn()
'ChangeControlsLanguage()
Trans = New Transport
Trans.ProjectId = CboProjects.SelectedValue
RefreshGridAfterProjectIdChanged()
AddHandler CboProjects.SelectedIndexChanged, AddressOf CboProjects_SelectedIndexChanged
End Try
End Sub
PopulateGrid method:
Public Sub PopulateGrid()
Try
If IsNothing(Trans.ListByProjectId_checkifanyrows()) Then
Me.BeginInvoke(New MethodInvoker(AddressOf Me.Close))
Else
Else
dgvTransport.DataSource = Nothing 'Clean datagrid before new data
dgvTransport.Refresh()
dgvTransport.DataSource = Trans.ListByProjectId()
AlignGrid()
PlaceImages()
End If
End Try
End Sub
Align method - placed within PopulateGrid method as you see when dgvTransport as set to nohing and then its detasource set to new data: Trans.ListByProjectId() now i am aligning it - hiding some columns etc... As you can see also i am adding this new image column... :
Private Sub AlignGrid()
Try
dgvTransport.Columns(0).Visible = False 'Project id
dgvTransport.Columns(1).Visible = False 'Id
dgvTransport.Columns(2).Visible = True 'Lp
dgvTransport.Columns(3).Visible = True 'Status
dgvTransport.Columns(4).Visible = True 'Dataprzyjazdu
dgvTransport.Columns(5).Visible = True 'DataRozpoczeciaZaladunku
dgvTransport.Columns(6).Visible = True 'DataOdjazdu
dgvTransport.Columns(7).Visible = True 'TypTransportu dgvTransport.Columns(8).Visible = False 'TypKontenera (reprezentacja liczbowa z tabeli tbTransport)
dgvTransport.Columns(9).Visible = True 'NumerKontenera
dgvTransport.Columns(10).Visible = True 'NumerCiezarowki
dgvTransport.Columns(11).Visible = True 'Plomba
dgvTransport.Columns(12).Visible = False 'Kierowca
dgvTransport.Columns(13).Visible = False 'Opis
dgvTransport.Columns(14).Visible = False 'Nazwa (nazwa projektu)
dgvTransport.Columns(15).Visible = True 'TypKontenera (reprezentacja za pomoca nazwy ze zlaczenia INNER JOIN))
Dim img As DataGridViewImageColumn = New DataGridViewImageColumn()
img.HeaderText = "Status2"
img.Name = "Status2"
dgvTransport.Columns.Insert(16, img)
'potrzebny aby zadzialalo: DgvMach.ColumnHeadersDefaultCellStyle.BackColor = Color.Gold
dgvTransport.EnableHeadersVisualStyles = False
'headers look
With dgvTransport.ColumnHeadersDefaultCellStyle
'The way to do this is to set the EnableHeadersVisualStyles flag for the data grid view to False, and set the background colour via the ColumnHeadersDefaultCellStyle.BackColor property. For example, to set the background colour to blue, use the following (or set in the designer if you prefer):
'If you do not set the EnableHeadersVisualStyles flag to False, then the changes you make to the style of the header will not take effect, as the grid will use the style from the current users default theme. The MSDN documentation for this property is here.
.BackColor = Color.White
.ForeColor = Color.Black
.Font = New Font("Ariel", 10, FontStyle.Regular)
.Alignment = DataGridViewContentAlignment.MiddleCenter
End With
dgvTransport.AllowUserToAddRows = False
dgvTransport.[ReadOnly] = True
dgvTransport.MultiSelect = False
dgvTransport.SelectionMode = DataGridViewSelectionMode.FullRowSelect 'zaznacza caly wiersz po kliknieciu
dgvTransport.AutoSizeColumnsMode = DataGridViewAutoSizeColumnMode.Fill 'WAZNE!!!: RESIZUJE CALY CONTENT GRIDA NIE ZOSSTAWIAJAC CIEMNEGO TLA !!!
dgvTransport.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
With dgvTransport.DefaultCellStyle
.BackColor = Color.White
.ForeColor = Color.Black
.Font = New Font("Ariel", 9, FontStyle.Regular)
.Alignment = DataGridViewContentAlignment.MiddleCenter
End With
'This will disable row autosizing and manual row resizing. To set the row height you can use the Height and MinimumHeight properties of the RowTemplate.
dgvTransport.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None
dgvTransport.AllowUserToResizeRows = False
End Sub
FrmTransportView_Shown event:
Private Sub FrmTransportView_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
AddHandler dgvTransport.SelectionChanged, AddressOf dgvTransport_SelectionChanged
End Sub
RefreshGridAfterProjectIdChanged
Public Sub RefreshGridAfterProjectIdChanged()
Trans.ProjectId = CboProjects.SelectedValue
PopulateGrid()
End Sub
CboProjects_SelectedIndexChanged
Private Sub CboProjects_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CboProjects.SelectedIndexChanged
RefreshGridAfterProjectIdChanged()
End Sub
PlaceImages
Private Sub PlaceImages()
For i As Integer = 0 To dgvTransport.Rows.Count - 1
Dim sHeader As String = dgvTransport.Columns(16).Name
If sHeader = "Status2" Then
Dim LINK = dgvTransport.Rows(i).Cells(8).Value
If LINK.ToString.Contains("1") Then
Dim Img As New DataGridViewImageCell
Img.Value = My.Resources._1
dgvTransport.Rows(i).Cells(16).Value = Img.Value
End If
If LINK.ToString.Contains("2") Then
Dim Img As New DataGridViewImageCell
Img.Value = My.Resources._2
dgvTransport.Rows(i).Cells(16).Value = Img.Value
End If
If LINK.ToString.Contains("3") Then
Dim Img As New DataGridViewImageCell
Img.Value = My.Resources._3
dgvTransport.Rows(i).Cells(16).Value = Img.Value
End If
End If
Next
End Sub
That's how it looks for first form load: (strange thing is column index of image column (Status2) is 0 - should be 16 as i placed index for that column to 16 in Align method.. As you see also not images are showing up...
Now when i am going to change selection in combobox:
of course as you can note from code this method handler is called: CboProjects_SelectedIndexChanged
so then again grid datasource will be set to Nothing, populate again, then align gird..
and that's what is shown after: (see indexes - also some image show up and column "Status2" was moved to left..)
3rd combobox change etc...
Hope to find someone here to help me out as i really have no idea how to fix that. Hope everything is clear.
Sorry this is coming a little late. Just a suggestion - I didn't experiment with it. Try to insert the DataGridImageColumn first before editing the visibility of the other columns.
Not too related, but in your AlignGrid sub, the static properties like ".BackColor" can be set on design just to reduce code.
Let us know your observations.
Thanks

Handling events of objects that were created in lists

I'm making a Scrabble game in Visual Basic. In my project, I created a list of labels that will make up my "tiles" of the game board. I'm just wondering how I would handle events for labels in the list, because they aren't objects that I created in the designer. (ex: Click, Hover events)
Here is the code that creates the grid of labels:
Dim labels As New List(Of Label)
For i = 0 To 10
For y = 0 To 10
Dim temp As New Label
With temp
Dim nfont As New Font("Fixedsys Excelsior 2.00", 15)
.Name = Str(i)
.AllowDrop = True
.BackColor = Color.White
.Location = New Point(y * 55 + 465, i * 45)
.Size = New System.Drawing.Size(50, 50)
.Visible = True
.Image = Image.FromFile("E:\Scrabble\Images\Blank.png")
.CreateControl()
.TextAlign = ContentAlignment.MiddleCenter
.ForeColor = Color.LimeGreen
.Font = nfont
End With
Me.Controls.Add(temp)
labels.Add(temp)
Next
Next
You would add the handler before adding the label in the list:
AddHandler temp.Click, AddressOf LabelClickHandler
Me.Controls.Add(temp)
labels.Add(temp)

Make and change TableLayoutPanel at runtime in WinForms

I would like to know how to make and change a TableLayoutPanel at runtime in VB.NET, WinForms.
I've had a look at the MSDN documentation but I can't seem to understand how to vary the number of columns/rows (ie create new ones) nor how to change the values of any of the cells.
My aim is to have a 4x4 grid that contains 16 labels, whose text comes from a multi-dimensional (4x4) integer array.
My current code is:
Dim table As New TableLayoutPanel
table.ColumnCount = 4
table.RowCount = 4
table.RowStyles.Add(New RowStyle(SizeType.Absolute, 8.0F))
This is based off the MSDN examples, but I'm not sure how to use the RowStyles.Add(several arguments) method. Can anyone explain it?
The following will create a TableLayoutPanel and all labels at run time. It is fully adjustable, in that you have have a 2 dimensional array of any size and it will display all values within that array. Using this code example should show you how to add rows and columns to a TableLayoutPanel dynamically at runtime.
Public Class Form1
Friend WithEvents TableLayout As TableLayoutPanel
Private DataArray(,) As Integer = New Integer(3, 3) {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
Me.AutoSize = True
TableLayout = New TableLayoutPanel
With TableLayout
.Name = "tableLayout"
.Margin = New System.Windows.Forms.Padding(0, 0, 0, 0)
.ColumnCount = 0
.RowCount = 0
.Dock = DockStyle.Fill
.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
.AutoSize = True
End With
Me.Controls.Add(TableLayout)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
For x = LBound(DataArray, 1) To UBound(DataArray, 1)
Me.TableLayout.ColumnCount += 1
Me.TableLayout.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
For y = LBound(DataArray, 2) To UBound(DataArray, 2)
If y = LBound(DataArray, 2) Then
Me.TableLayout.RowCount += 1
Me.TableLayout.RowStyles.Add(New ColumnStyle(SizeType.AutoSize))
End If
Dim lbl = New Label
With lbl
.Name = "lbl" & x & y
.TextAlign = ContentAlignment.MiddleCenter
.Text = "Value: " & DataArray.GetValue(x, y)
.Dock = DockStyle.Fill
.AutoSize = True
End With
Me.TableLayout.Controls.Add(lbl, y, x)
Next
Next
End Sub
End Class
I suggest to you to create a TableLayoutPanel using Designer and after that to check the auto generated code in Designer.cs (Designer.vb in your case) class. Here small example in C#:
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 4;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Location = new System.Drawing.Point(252, 75);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(200, 100);
this.tableLayoutPanel1.TabIndex = 4;
To add Controls to your TableLayout use it Controls property. For example:
private void button2_Click(object sender, EventArgs e)
{
Label label = new Label();
label.Text = "Hello!";
tableLayoutPanel1.Controls.Add(label, 0, 0);
}
enter code here

Width of drawn list box?

I've created a custom combobox that uses a custom instance of a listbox control as the dropdown menu.
In order to customize the selection highlight of the listbox, I had to change its 'DrawMode' property to 'OwnerDrawFixed' and added the following code:
Private Sub _listBox_DrawItem(sender As Object, e As DrawItemEventArgs)
If e.Index >= 0 Then
e.DrawBackground()
If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
Using br = New LinearGradientBrush(e.Bounds, ColorSelectionListbox, ColorSelectionListbox, 0)
e.Graphics.FillRectangle(br, e.Bounds)
End Using
End If
Using b As New SolidBrush(ColorTextListbox)
e.Graphics.DrawString(_listBox.GetItemText(_listBox.Items(e.Index)), e.Font, b, e.Bounds)
End Using
e.DrawFocusRectangle()
RaiseEvent DrawItem(Me, e)
End If
End Sub
But with this, the width that I set for it is ignored and becomes a fixed width of 15 or so pixels.
How can I set the width of the owner-drawn control? Currently I have it as a property:
Public Property DropDownWidth() As Integer
Get
Return _dropDownWidth
End Get
Set(value As Integer)
_dropDownWidth = value
_listBox.Width = value
Invalidate(True)
End Set
End Property
Below is the rest of the code that is relevant, but nevermind.. I fixed the issue by setting the Autosize property of _controlHost to False. I had it set to True in order to display all items within the list (without having to define a dropdown maximum) but for some reason it behaves differently when the DrawMode is set to OwnerDraw.
_listBox = New ListBox()
_listBox.IntegralHeight = True
_listBox.BorderStyle = BorderStyle.FixedSingle
_listBox.SelectionMode = SelectionMode.One
_listBox.BindingContext = New BindingContext()
_dropDownWidth = Me.Width
_listBox.Width = _dropDownWidth
_controlHost = New ToolStripControlHost(_listBox)
_controlHost.Padding = New Padding(0)
_controlHost.Margin = New Padding(0)
_controlHost.AutoSize = False 'Used to be variable property _dropDownAutoSize
_popupControl = New ToolStripDropDown()
_popupControl.Padding = New Padding(0)
_popupControl.Margin = New Padding(0)
_popupControl.AutoSize = True '
_popupControl.DropShadowEnabled = False
_popupControl.Items.Add(_controlHost)

Adding checkbox to datagridview column header, not aligning properly

Im trying to add a checkbox to a specific datagridview column header, I found some code online to help but it's not aligning properly and I'm not really sure how to fix it.
Below is an image of the problem and the code, any help would be greatly appreciated!
P.S. I think it might be something to do with properties but I've played around with them but not been successful.
Private checkboxHeader231 As CheckBox
Private Sub show_chkBox()
Dim rect As Rectangle = DataGridView1.GetCellDisplayRectangle(columnIndexOfCheckBox, -1, True)
' set checkbox header to center of header cell. +1 pixel to position
rect.Y = 3
rect.X = rect.Location.X + 8 + (rect.Width / 4)
checkboxHeader231 = New CheckBox()
With checkboxHeader231
.BackColor = Color.Transparent
End With
checkboxHeader231.Name = "checkboxHeader1"
checkboxHeader231.Size = New Size(18, 18)
checkboxHeader231.Location = rect.Location
AddHandler checkboxHeader231.CheckedChanged, AddressOf checkboxHeader231_CheckedChanged
DataGridView1.Controls.Add(checkboxHeader231)
End Sub
Private Sub checkboxHeader231_CheckedChanged(sender As System.Object, e As System.EventArgs)
Dim headerBox As CheckBox = DirectCast(DataGridView1.Controls.Find("checkboxHeader1", True)(0), CheckBox)
For Each row As DataGridViewRow In DataGridView1.Rows
row.Cells(columnIndexOfCheckBox).Value = headerBox.Checked
Next
End Sub
This is my first entry, but I think this is what youre looking for. I tested it and it worked on my datagrid. You were using the width for the rectangle, youll need it for the column width instead. I set the column header to 4, but you would replace the 4 with your column you want to use I put it in two ways, one with a four loop, the other just as single lines. Tell me if this worked for you:
Dim rect As Rectangle = DataGridView1.GetCellDisplayRectangle(4, -1, True) ' replace 4
rect.Y = 3
Dim sum = DataGridView1.Columns(0).Width
'for this area write a for loop to find the width of each column except for the last line which you manually do
'
'
'For i As Integer = 1 To 4 - 1 Step 1 ' replace 4
'sum = sum + DataGridView1.Columns(i).Width
'Next
sum = sum + DataGridView1.Columns(1).Width
sum = sum + DataGridView1.Columns(2).Width
sum = sum + DataGridView1.Columns(3).Width
' stop here and add the last line by hand here
sum = sum + (DataGridView1.Columns(4).Width / 2) + 35 ' used in both cases ' replace 4
rect.X = sum
checkboxHeader231 = New CheckBox()
With checkboxHeader231
.BackColor = Color.Transparent
End With
checkboxHeader231.Name = "checkboxHeader1"
checkboxHeader231.Size = New Size(18, 18)
checkboxHeader231.Location = rect.Location
AddHandler checkboxHeader231.CheckedChanged, AddressOf checkboxHeader231_CheckedChanged
DataGridView1.Controls.Add(checkboxHeader231)
Private headerBox As CheckBox
Private Sub show_checkBox()
Dim checkboxHeader As CheckBox = New CheckBox()
Dim rect As Rectangle = PendingApprovalServiceListingDataGridView.GetCellDisplayRectangle(4, -1, True)
rect.X = 20
rect.Y = 12
With checkboxHeader
.BackColor = Color.Transparent
End With
checkboxHeader.Name = "checkboxHeader"
checkboxHeader.Size = New Size(14, 14)
checkboxHeader.Location = rect.Location
AddHandler checkboxHeader.CheckedChanged, AddressOf checkboxHeader_CheckedChanged
PendingApprovalServiceListingDataGridView.Controls.Add(checkboxHeader)
End Sub
Private Sub checkboxHeader_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs)
headerBox = DirectCast(PendingApprovalServiceListingDataGridView.Controls.Find("checkboxHeader", True)(0), CheckBox)
For Each row As DataGridViewRow In PendingApprovalServiceListingDataGridView.Rows
row.Cells(0).Value = headerBox.Checked
Next
End Sub