【问题标题】:VB.NET - Customizing Listview/ListboxVB.NET - 自定义列表视图/列表框
【发布时间】:2016-06-26 23:56:26
【问题描述】:

所以有一些已回答的问题,但我仍然不明白我的意思。比如我怎样才能基本上让我的 Listview 非常自定义。

我试图让它看起来像的是:

正如您所看到的,它是高度定制的 - 如果您想知道,我在 Photoshop 中制作了这个,我现在并不完全需要定制的滚动条或悬停高亮效果,但将来需要一种方法来做到这一点.

有什么想法可以实现这一点吗?

编辑:我能得到的最接近的是: 但是有一个“很少”的问题 - 我无法让文本对齐/位置看起来正确,因为我需要在文本之前放置 2 个空格才能让它看起来不错,这在未来尝试获取时“可能”会给我带来问题点击操作。接下来,由于某种原因,无法选择使用边框/网格,它消除了在第一张图像中的框周围执行稍深灰色边框的能力。有什么想法吗?

(我以前是这样弄的:http://www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment)

编辑 2:重新编辑:好的,我认为我已经对突出显示颜色进行了排序,但它看起来不像我使用 FillRectangle 但无论我如何放置边界(这是正确的),它似乎会留下额外的 1px白色的边界大约 1-2 秒后刷新并消失。无论如何要解决这个问题?

编辑 3:我在“几乎”完成它的地方使用的 http://www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment) ListBox 对我不起作用,因为它只是不稳定 - 如果我在设计模式下制作 ColorListBox 后编辑任何东西,它就不会工作并给我带来烦人的错误。它还具有“SelectedItem”参数,不再是破坏我一半代码的对象。除此之外,如果这两个是可修复的,我想它会起作用,但我不知道如何修复它:(

所以我现在恢复到一个非常基本的列表框,只有文本,直到你们可以帮助找到一种方法来自定义它,就像上面的第一张图片一样。

【问题讨论】:

  • 你可以只添加一个空的 subItem 并相应地设置列表视图的列宽 ..ListView1.Columns.Item(1).Width = 5 或任何适合你的值
  • 就刷新而言,在导致刷新的子中,只需在子的开头添加listview1.SuspendLayout,并在末尾添加listview1.ResumeLayout - 将listview1替换为当然是您的 ListView 的名称
  • @DavidWilson 对于您发表的第一条评论,我假设您将图像添加到非自定义列表视图,对吗?第二条评论也可以解决这个问题:i.gyazo.com/e788a32b9cd052be1b9685cf42f22556.gif
  • @DavidWilson 哦,没关系 - 我明白你在说什么 - 第一条评论是关于间距问题 - 看起来不错,我可以试一试。但是对于第二条评论,它似乎不起作用 D:无论如何,我决定放弃使用它,因为每次我在 Designer 中更改某些内容时,它都会添加“Pragma”。到 Designer.vb 中的 ColorBox 事物的开头,所以它太烦人了。无论如何我可以独立完成这个或其他什么?
  • ListView 不会是我的第一个(甚至最后一个)选择。你可能能够得到大部分你想要的东西just using a Button。要获得真正的花哨,build a UserControl 或 AutoScroll 面板上的自定义控件将允许您做任何您想做的事情。由于不同的操作系统、主题和视觉风格,您的图像不会使您所描述的内容非常明显。很难说操作系统应用了什么,什么是特殊的。

标签: .net vb.net listbox


【解决方案1】:

完全不清楚你到底想要什么。 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 而不是子项目时才会点亮)。李>
  • 那个长方形的深灰色是绝对颜色吗? HotLightSelected 着色通常由操作系统(==操作系统)根据用户的主题和颜色选择来处理。这个小工具是否应该忽略这些,或者您的主题是否为突出显示使用了某种灰色阴影?

我仔细测量了第一张图片中的元素以获得一些指标,然后对上述答案进行了猜测。

无代码解决方案

使用按钮。由于用户可能会单击其中一个按钮来选择所需的频道,因此ButtonListView 更有意义(更多)。您可以在Button 上显示图像以及文本,并使用FlatAppearance 属性来设置您想要的样式。使用Tag跟踪频道ID或集合中相关频道的索引。

最后,使用MouseHoverMouseLeave 事件来操作FlatAppearance.BorderSizeFlatAppearance.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。使用LabelPictureBox 以及正常事件,您几乎可以让它做任何您想做的事情。你可以在大约 20 分钟内一起拍一张:

Channel 用户控件包含在自动滚动的Panel 中。它们基本上是一个自定义按钮,但允许您对布局、行为和外观进行大量控制。最重要的是,每个都有自己独特的Click 事件。


1原因在 LV 或 LB 中的 item 之间没有间隙是为了让用户更容易选择 item。排水沟或间隙打开了用户点击那里但没有得到任何结果的机会。或者根据实现,您的代码会因使用错误索引而崩溃。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-25
    • 2023-04-06
    • 2023-03-09
    • 2016-05-19
    • 1970-01-01
    • 2015-12-22
    • 2018-05-10
    • 2020-12-23
    相关资源
    最近更新 更多