完全不清楚你到底想要什么。 Like how can I basically make my Listview very customized 模糊且非常广泛(以及为什么您没有得到您喜欢的答案)。
图片很好,但描述所需外观的文字会更好。给定一张要仔细检查的图片,没有办法知道什么是重要的,什么就在那里……因为。根据very customized,我认为每个细节都很重要。另外:
-
Listview/Listbox 选择一个:它们是非常不同的控件。
- 项目之间的小 2 像素边距对于
ListView 来说会很成问题,所以我划掉了 LV1。
- 为什么第一张图片中的所有项目都是灰色的?这通常表示已禁用,那么是否可以禁用项目(例如当给定频道没有节目数据时)?
- “FOX”上的灰色框代表什么?是被选中的项目吗?它是 HotLight/MouseHover 项目吗?
ListBox/ListView 项目在鼠标经过时通常不会点亮(当 HotTracking 为真时,LV 可以,但只有当鼠标经过 Item 而不是子项目时才会点亮)。李>
- 那个长方形的深灰色是绝对颜色吗?
HotLight 和 Selected 着色通常由操作系统(==操作系统)根据用户的主题和颜色选择来处理。这个小工具是否应该忽略这些,或者您的主题是否为突出显示使用了某种灰色阴影?
我仔细测量了第一张图片中的元素以获得一些指标,然后对上述答案进行了猜测。
无代码解决方案
使用按钮。由于用户可能会单击其中一个按钮来选择所需的频道,因此Button 比ListView 更有意义(更多)。您可以在Button 上显示图像以及文本,并使用FlatAppearance 属性来设置您想要的样式。使用Tag跟踪频道ID或集合中相关频道的索引。
最后,使用MouseHover 和MouseLeave 事件来操作FlatAppearance.BorderSize 和FlatAppearance.BorderColor 以非常接近问题中的第一个图像:
它们驻留在滚动的Panel 中。面板宽度只是比控件宽一点,以避免水平滚动条。至于按钮,HotLight 边框 (???) 围绕整个控件而不仅仅是文本。不是您想要的图片显示的内容,但另一方面,除了一些标准属性和一些事件处理代码(5-6 行)之外,没有其他任何内容。
图像列表框
ownerdraw ListBox 会让你更接近你想要的,但最终这只会让它看起来像一个ListBox,其中包含Buttons。
在表单上放置一个 ListBox,并设置以下属性:
- DrawMode = OwnerDrawFixed
- 积分高度 = 假
- Itemheight = 64(这是基于第一张图片中的图片为 60x60 的事实)
- 根据需要将背景颜色设置为ControlLight 或{233,233,233},以获得确切的灰色阴影。
如前所述,ListBox 项目在鼠标悬停时通常不会亮起,因此我们需要一些代码来跟踪它(例如 Button):
Private mouseItem As Int32 = -1
Private Sub lbChannels_MouseMove(sender As Object,
e As MouseEventArgs) Handles lbChannels.MouseMove
Dim ndx = lbChannels.IndexFromPoint(e.Location)
' test to avoid millions of paints
If ndx <> mouseItem Then
If mouseItem <> -1 Then
' invalidate/redraw the OLD itemrect
lbChannels.Invalidate(lbChannels.GetItemRectangle(mouseItem))
End If
mouseItem = ndx
' invalidate/redraw the NEW itemrect
lbChannels.Invalidate(lbChannels.GetItemRectangle(mouseItem))
End If
End Sub
Private Sub lbChannels_MouseLeave(sender As Object,
e As EventArgs) Handles lbChannels.MouseLeave
If mouseItem <> -1 Then
' get rect for what wont be the hot item in a tick
Dim rect = lbChannels.GetItemRectangle(mouseItem)
'lbChannels.Invalidate()
mouseItem = -1 ' no longer Hot
lbChannels.Invalidate(rect)
End If
End Sub
更新,通过仅重绘已更改的项目来最大程度地减少闪烁。
我没有使用MouseHover 事件,因为它稍后会触发,导致提到的延迟很小(但没有接近一秒)。 MouseMove 还可以更轻松地获取鼠标位置。
IImageItem
基于 cmets,您希望根据应用中的一些其他数据自动生成项目。与其假设该类是什么样子并使其易于实现并适用于任何类,不如使用一个接口:
Public Interface IImageItem
Property ID As String ' ???
Property ItemImage As Image
Property Text As String
End Interface
绘制代码将要求实现该接口,以便它可以使用这些属性来绘制您的项目。 Id 是一个额外的方法,它允许链接从集合中选择或单击的 ListBox 项目(仅当您将项目添加到 ListBox 时才需要 - 不推荐)。如果需要,它可以扩展为包含 Enabled 属性以不同方式绘制它们。因此,您将在您的集合项目类上实现此接口:
Public Class ChannelItem
Implements IImageItem
Public Property ItemImage As Image Implements IImageItem.ItemImage
Public Property Text As String Implements IImageItem.Text
Public Property ID As String Implements IImageItem.ID
' + your existing properties
Public Sub New(txt As String, img As Image, key As String)
Text = txt
ItemImage = img
ID = key
End Sub
Public Overrides Function ToString() As String
Return Text
End Function
End Class
注意:ChannelItem 是您用于跟踪频道的任何类的 my 演示版本。只需在您的类中输入Implements IImageItem,然后按回车键,IDE 就会添加属性,在将项目添加到您的集合之前设置它们。
绘制项目代码
Private Sub lbChannels_DrawItem(sender As Object,
e As DrawItemEventArgs) Handles lbChannels.DrawItem
Dim lb As ListBox = lbChannels
If e.Index < 0 Then
TextRenderer.DrawText(e.Graphics, "", lb.Font, e.Bounds, lb.ForeColor)
Return
End If
Dim iItem As IImageItem
If TypeOf (lb.Items(e.Index)) Is IImageItem Then
iItem = DirectCast(lb.Items(e.Index), IImageItem)
Else
TextRenderer.DrawText(e.Graphics, lb.Items(e.Index).ToString,
lb.Font, e.Bounds, lb.ForeColor)
Return
End If
Dim imgRect As Rectangle = Rectangle.Empty
Dim txtRect As Rectangle
' calc
If iItem.ItemImage IsNot Nothing Then
imgRect = New Rectangle(e.Bounds.X + 1, e.Bounds.Y + 1,
iItem.ItemImage.Width + 2, iItem.ItemImage.Height + 2)
End If
' GetTextExtent
Dim sz = TextRenderer.MeasureText(" " & iItem.Text, lb.Font)
txtRect = New Rectangle(iItem.ItemImage.Width + 4, e.Bounds.Y + 1,
(e.Bounds.Width - iItem.ItemImage.Width) - 8,
e.Bounds.Height - 2)
' Draw Big Box around the text portion
If e.Index = mouseItem Then
Using pR As New Pen(SystemColors.ControlDark, 2),
brB As New SolidBrush(SystemColors.Window)
e.Graphics.DrawRectangle(pR, txtRect)
txtRect.Inflate(-1, -1)
e.Graphics.FillRectangle(brB, txtRect)
End Using
ElseIf (e.State.HasFlag(DrawItemState.Selected)) Then
' ToDo: modify for whatever is desired for the selected item
' this is a guess/example
Using pR As New Pen(SystemColors.Highlight, 2),
brB As New SolidBrush(SystemColors.Window)
e.Graphics.DrawRectangle(pR, txtRect)
txtRect.Inflate(-1, -1)
e.Graphics.FillRectangle(brB, txtRect)
End Using
Else
' could use a channel specific color for each BG
' just extend IImageItem
e.DrawBackground()
End If
If iItem.ItemImage IsNot Nothing Then
e.Graphics.DrawImage(iItem.ItemImage, imgRect)
End If
' recalc TR for where the text really goes
txtRect = New Rectangle(iItem.ItemImage.Width + 4, e.Bounds.Y + 1,
sz.Width + 2, e.Bounds.Height - 0)
TextRenderer.DrawText(e.Graphics, iItem.Text, lb.Font, txtRect, lb.ForeColor)
End Sub
同样,不清楚“FOX”的深灰色框是否代表 SelectedItem,HotLight`/Hover 项目甚至禁用。该代码显示了如何/在哪里为前 2 种情况设置文本框。根据需要进行修改。
用法
' a collection of ChannelItem objects (which implement IImageItem)
Dim channels As New List(Of ChannelItem)
' my fake data
channels.Add(New ChannelItem("More 4", My.Resources.SO_LVImg01, "M4"))
channels.Add(New ChannelItem("Channel 4", My.Resources.SO_LVImg02, "4"))
channels.Add(New ChannelItem("RTE One", My.Resources.SO_LVImg03, "RET1"))
channels.Add(New ChannelItem("FOX", My.Resources.SO_LVImg04, "Fox"))
...
这一切都很简单,只需将您的类替换为ChannelItem(它必须实现IImageItem)。您可以将该集合用作DataSource,而不是将项目复制到项目集合中:
lbChannels.DataSource = channels
SelectedValue 将是一个ChannelItem 对象,被装箱为System.Object,将其转换回以获取所有相关数据,例如 URL 或 正在播放文本。
结果:
在这种情况下,选定的项目有一个SystemColor.Hightlight 框(在我的例子中是蓝色的),鼠标悬停的项目有一个灰色的框。与原始图像一样,每个项目之间甚至似乎存在 2px 的小间隙。这是图像高度比使用的ItemHeight 略低的结果。
在事物上设置边框将无需鼠标悬停突出显示。
用户控制
如果您希望(几乎)完全控制这些伪按钮的显示方式,您可能应该构建一个UserControl。使用Label 和PictureBox 以及正常事件,您几乎可以让它做任何您想做的事情。你可以在大约 20 分钟内一起拍一张:
Channel 用户控件包含在自动滚动的Panel 中。它们基本上是一个自定义按钮,但允许您对布局、行为和外观进行大量控制。最重要的是,每个都有自己独特的Click 事件。
1原因在 LV 或 LB 中的 item 之间没有间隙是为了让用户更容易选择 item。排水沟或间隙打开了用户点击那里但没有得到任何结果的机会。或者根据实现,您的代码会因使用错误索引而崩溃。