Width of drawn list box? - vb.net

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)

Related

Unexpected TableLayoutPanel Behavour

Hi having some issues with some nested tablelayoutpanels in a custom control
I have a tablelayoutpanel in a tablelayoutpanel in a tablelayoutpanel Crazy i know but it keeps it uniform and ordered
The custom control consists of 2 tablelayoutpanels that is placed in a table layout panel on a form and in a preview area of my main form.
Having them set at design time works fine but dynamically adding / removing the topmost ones with 1 row 2 columns into the one below it that has 1 column and x rows only Seems to break the autosizing behavior I'm chasing.
So I want the cells & rows to resize automatically based on the contents in this case labels but still remain in a neat ordered layout
There's no docking anywhere in the hierarchy of controls just anchors here and there
Here's my code to add the tablelayoutpanels below
"https://i.stack.imgur.com/vRfhE.png"
Private Sub AddControl(ByRef Counter As Counter)
Dim Gpanel As New TableLayoutPanel
Dim tlabel As New Label
Dim clabel As New Label
Dim pad As Integer = Counter.Cpad
TLPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink
Gpanel.AutoSizeMode = AutoSizeMode.GrowAndShrink
Gpanel.AutoSize = True
Gpanel.GrowStyle = TableLayoutPanelGrowStyle.AddRows
Gpanel.BorderStyle = BorderStyle.FixedSingle
Gpanel.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
TLPanel.BorderStyle = BorderStyle.FixedSingle
Gpanel.Dock = DockStyle.None
Gpanel.Padding = New Padding(0)
Gpanel.Anchor = AnchorStyles.Top Or AnchorStyles.Left
Gpanel.BackColor = Color.Transparent
Gpanel.RowCount = 1
Gpanel.ColumnCount = 2
Gpanel.RowStyles.Add(New RowStyle(SizeType.AutoSize))
Gpanel.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
Gpanel.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
tlabel.Dock = DockStyle.None
clabel.Dock = DockStyle.None
tlabel.GetPreferredSize(Size.Empty)
clabel.GetPreferredSize(Size.Empty)
tlabel.Text = Counter.Clabel
clabel.Text = Counter.Ccount.ToString
tlabel.Padding = New Padding(pad)
clabel.Padding = New Padding(pad)
tlabel.Anchor = AnchorStyles.None
clabel.Anchor = AnchorStyles.None
tlabel.ForeColor = Color.FromName(Counter.Clcolor)
clabel.ForeColor = Color.FromName(Counter.Ccolor)
Dim fontstyle As New FontStyle
fontstyle = Counter.ClfontStyle
tlabel.Font = New Font(Counter.Clfont, Counter.Clfontsize, fontstyle)
fontstyle = Counter.CcfontStyle
clabel.Font = New Font(Counter.Ccfont, Counter.Ccfontsize, fontstyle)
Gpanel.Controls.Add(tlabel, 0, 0)
Gpanel.Controls.Add(clabel, 1, 0)
TLPanel.Controls.Add(Gpanel, 0, Counter.ID)
TLPanel.RowCount += 1
TLPanel.RowStyles.Add(New RowStyle(SizeType.AutoSize))
Dim Styles As TableLayoutRowStyleCollection = TLPanel.RowStyles
Dim Cstyles As TableLayoutColumnStyleCollection = Gpanel.ColumnStyles
Gpanel.RowStyles.Item(0) = New RowStyle(SizeType.AutoSize)
TLPanel.ColumnStyles.Item(0) = New ColumnStyle(SizeType.AutoSize)
For i = 0 To Cstyles.Count - 1
Cstyles.Item(i) = New ColumnStyle(SizeType.AutoSize)
Next
For i = 0 To Styles.Count - 1
Styles.Item(i) = New RowStyle(SizeType.AutoSize)
Next
TLPanel.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
AutoSize = True
AutoSizeMode = AutoSizeMode.GrowAndShrink
End Sub
'

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

Visual Basic Datagrid View change row colour

Every other change to the datagrid view works fine but for some reason the row color just wont change.
Ive debugged and my application goes through the loop to change the row color.
Also I have a button that gives the datagrid view a new list and colors the rows accordingly, when I click the button the row colors changes do work!
Public Sub New(measuredValues As List(Of MeasuredValuesModel), valueType As ValueType)
IsFiltered = False
' This call is required by the designer.
InitializeComponent()
MeasuredValuesList = measuredValues
uxGrid.DataSource = MeasuredValuesList
uxGrid.Columns("StepID").Visible = False
uxGrid.Font = New Font("Arial", 10, FontStyle.Bold)
For Each c As DataGridViewColumn In uxGrid.Columns
Dim Column As DataGridViewColumn = c
c.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
Next
If valueType = StepItem.ValueType.CalculatedValues Then
uxButtonFilter.Visible = False
uxGrid.Columns("DISPOSITION").Visible = False
End If
For Each gridRow As DataGridViewRow In uxGrid.Rows
If gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "Y" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Red
ElseIf gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "N" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Orange
End If
Next
uxStepID.Text = MeasuredValuesList.FirstOrDefault.StepID
' Add any initialization after the InitializeComponent() call.
'TODO binding
End Sub
Here is the button that works for changing row color.
Maybe it works because it's pressed after the grid has been created? Im not too sure.
Private Sub uxButtonFilter_Click(sender As Object, e As EventArgs) Handles uxButtonFilter.Click
If IsFiltered = True Then
uxGrid.DataSource = MeasuredValuesList
For Each gridRow As DataGridViewRow In uxGrid.Rows
If gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "Y" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Red
ElseIf gridRow.Cells("PASSFAIL").Value.ToString() = "FAIL" And (gridRow.Cells("DISPOSITION").Value.ToString() = "N" Or gridRow.Cells("DISPOSITION").Value.ToString() = "N/A") Then
gridRow.DefaultCellStyle.BackColor = Color.Orange
End If
Next
Its the exact same loop but it seems to work fine when i use the button.
Is it possible that your datagridview isn't loaded fully when you try to recolor the rows?
Since you are setting the datasource, you should put your code that affects the grid after you can make sure that it is finished loading. The column widths change because it is not dependent on the data in the grid, but your colouring is.
Catch the uxGrid.databindingComplete event and try colouring the rows in there

ComboBox DrawItem event handling for Font Combo Box - WinForms, VB.NET

I am creating a combobox which shows fonts in the list, with every font item displayed in its own font style. The combobox loads without errors but the list that shows is all in just one font. I have set DrawMode = OwnerDrawVariable and IntegralHeight = false, but still no result. The DrawItem code that you can see here is from the MSDN website, with a few changes as per my requirement. Here is my code:
BindCOmboboxes() is called when the form loads.
Private Sub w_BindComboBoxes() (StyleControl is a User Control containing the combobox)
'Set Properties
Dim tFont As Font = Nothing
StyleControl.TS1Font.DrawMode = DrawMode.OwnerDrawVariable
For Each fntfam As FontFamily In FontFamily.Families
If fntfam.IsStyleAvailable(FontStyle.Regular) Then
tFont = New Font(fntfam, 12, FontStyle.Regular)
StyleControl.TS1Font.Items.Add(tFont.Name & ",Regular")
End If
If fntfam.IsStyleAvailable(FontStyle.Italic) Then
tFont = New Font(fntfam, 12, FontStyle.Italic)
StyleControl.TS1Font.Items.Add(tFont.Name & ",Italic")
End If
If fntfam.IsStyleAvailable(FontStyle.Bold) Then
tFont = New Font(fntfam, 12, FontStyle.Bold)
StyleControl.TS1Font.Items.Add(tFont.Name & ",Bold")
End If
Next
End Sub
DrawItem function for TS1Font combobox
Private Sub FontComboBoxDrawItems(sender As Object, e As DrawItemEventArgs)
Dim size As Integer = 12
Dim myFont As System.Drawing.Font
Dim family As FontFamily = new FontFamily("Arial", 12) <-- to avoid crash
Dim fntStyle As FontStyle = FontStyle.Regular <-- To avoid crash
Dim animalColor As System.Drawing.Color = Color.Black
Dim str As String = DirectCast(sender, ComboBox).Items(e.Index).ToString.Trim
Dim brk() As String = str.Split(",")
For Each fam As FontFamily In FontFamily.Families
If fam.Name = brk(0) Then family = fam
Next
Select Case brk(1)
Case "Regular"
fntStyle = FontStyle.Regular
Case "Bold"
fntStyle = FontStyle.Bold
Case "Italic"
fntStyle = FontStyle.Italic
End Select
' Draw the background of the item.
e.DrawBackground()
' Draw each string in the array, using a different size, color,
' and font for each item.
myFont = New Font(family, size, fntStyle)
e.Graphics.DrawString(DirectCast(sender, ComboBox).Items(e.Index).ToString, myFont, System.Drawing.Brushes.Black,
New PointF(e.Bounds.X, e.Bounds.Y))
' Draw the focus rectangle if the mouse hovers over an item.
e.DrawFocusRectangle()
End Sub
On debugging in the DrawItem function, the values in the statement e.Graphics.DrawString = shows proper font family, style and size values. So why doesn't it draw the items that way?
Result:

List view sort arrow vb.net

i am trying to draw sort arrows on list view column header along with the default visual styles
so far i have got this
Private Sub List_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles List.DrawColumnHeader
e.DrawDefault = True
If e.ColumnIndex = selectedIndex Then
e.Graphics.DrawImage(ImageList1.Images(1), CType(e.Bounds.Left + e.Bounds.Width / 2, Single) - 5, -2)
End If
End Sub
but the visual style is drawn over the arrow somehow
so i figured i could try this :
Private Sub List_DrawColumnHeader(sender As Object, e As DrawListViewColumnHeaderEventArgs) Handles List.DrawColumnHeader
e.DrawDefault = True
If lastDrawn.ColumnIndex = selectedIndex Then
e.Graphics.DrawImage(ImageList1.Images(1), CType(lastDrawn.Bounds.Left + lastDrawn.Bounds.Width / 2, Single) - 5, -2)
End If
lastDrawn=e
End Sub
and it draws the arrow when the next corresponding column is being drawn
but with this i cant get it to draw for the last column
Screenshots:
In order to use the .NET build in solution for showing a custom icon for a list view column header you need to:
create an ImageList
add three images (up / down arrow and empty) to it
bind the image list to the ListView control
bind to the ColumnClick event of the ListView control
when sorting the columns set the ImageKey property of the currently sorted column depending on the sorting direction
This example class (a simple form) shows how to set the images correctly not using custom drawing for the ListView header columns.
It does not implement any sort! (how to implement a ListViewSorter is shown in this MSDN article)
You need to implement a custom ListView-Sorter class and retrieve the image or the image key from it, after a column is sorted.
Public Class SimpleForm
Inherits Form
Private sortItems = New ImageList()
Dim lv As ListView = New ListView()
Dim so = System.Windows.Forms.SortOrder.Ascending
Public Sub New()
' create columns, items and ListView
Dim columns = New List(Of ColumnHeader)
Dim c1 = New ColumnHeader()
c1.Name = "c1"
c1.Text = "Name"
Dim c2 = New ColumnHeader()
c2.Name = "c2"
c2.Text = "Type"
columns.Add(c1)
columns.Add(c2)
Dim items = New List(Of ListViewItem)
Dim i1 = New ListViewItem("Terminator")
i1.SubItems.Add("T1000")
Dim i2 = New ListViewItem("Terminator")
i2.SubItems.Add("T10")
Dim i3 = New ListViewItem("J.C.")
i3.SubItems.Add("Human")
items.Add(i1)
items.Add(i2)
items.Add(i3)
' init and bind column click
lv.Columns.AddRange(columns.ToArray())
lv.Items.AddRange(items.ToArray())
lv.SmallImageList = sortItems
lv.View = View.Details
lv.Dock = DockStyle.Fill
Controls.Add(lv)
AddHandler lv.ColumnClick, AddressOf clickEventHandler
' init images list
sortItems.TransparentColor = System.Drawing.Color.Transparent
sortItems.Images.Add("up", Image.FromFile("d:\temp\32\arrow_up.gif"))
sortItems.Images.Add("down", Image.FromFile("d:\temp\32\arrow_down.gif"))
sortItems.Images.Add("empty", Image.FromFile("d:\temp\32\check.gif"))
End Sub
Private Sub clickEventHandler(ByVal o As Object, ByVal e As ColumnClickEventArgs)
' Implement a custom ListViewItemSorter and fetch the icon from it!
' Set the ListViewItemSorter property to a new ListViewItemComparer
' object. Setting this property immediately sorts the
' ListView using the ListViewItemComparer object.
' THIS CODE SHOWS ONLY HOW TO SET THE SORT ICON!
For i As Integer = 0 To lv.Columns.Count - 1
If (i = e.Column) Then
Select Case (so)
Case System.Windows.Forms.SortOrder.Ascending
lv.Columns(i).ImageKey = "up"
so = System.Windows.Forms.SortOrder.Descending
Case System.Windows.Forms.SortOrder.Descending
lv.Columns(i).ImageKey = "down"
so = System.Windows.Forms.SortOrder.Ascending
Case Else
lv.Columns(i).ImageKey = "empty"
so = System.Windows.Forms.SortOrder.None
End Select
Else
lv.Columns(i).ImageKey = "empty"
End If
Next i
End Sub
End Class
The output looks like this: