VB.NET - 自定义Listview / Listbox

时间:2016-05-18 13:25:51

标签: .net vb.net listbox

所以有几个已回答的问题,但我仍然不明白我的意思。就像我怎么能基本上使我的Listview非常自定义。

我试图让它看起来像是:

1

正如你所看到的那样HEAVILLY定制 - 如果你想知道我在Photoshop中制作了这个,我现在不需要定制的Scrollbar或者现在的Hover-Highlight效果,但是在未来我需要一种方法来做到这一点

我是如何实现这一目标的?

编辑:我能得到的最接近的是: 2 但是有一个“FEW”问题 - 我不能让文本对齐/位置看起来正确,因为我需要在文本之前放置2个空格以使其实际上看起来不错,“MIGHT”在将来尝试获取时会给我带来问题点击动作。接下来,由于某种原因,没有选择使用边框/网格,这样就无法在第一张图像中删除在Boxes周围稍微变暗的灰色边框。任何想法的家伙?

(我过去常常这样做: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”参数,因为它不再是一个破坏我代码一半的Object。除此之外,如果那些2是可修复的,我想它会起作用,但我不知道如何解决它:(

所以我现在只使用文本恢复到非常基本的ListBox,直到你们可以帮助找到一种方法来定制它,就像上面的第一张图片一样。

1 个答案:

答案 0 :(得分:3)

完全不清楚你想要什么。 Like how can I basically make my Listview very customized模糊且非常广泛(为什么你没有得到你喜欢的答案)。

图片很不错,但描述所需外观的会更好。鉴于要仔细检查的图片,没有办法知道什么是重要的,什么是正确的......因为。我假设每个细节都很重要,基于very customized。另外:

  • Listview/Listbox 选择一个:它们是非常不同的控件。
  • 项目之间的小2像素边距对ListView非常有问题,所以我抓了LV 1
  • 为什么第一张图片中的所有项目都是灰色的?这通常表示已禁用,因此可以禁用项目(例如,当给定通道没有程序数据时)?
  • " FOX"上的灰色框是什么?代表?是选中的项目吗?它是HotLight / MouseHover项目吗?当鼠标经过时,ListBox / ListView项通常不会亮起(当HotTracking为真时LV可以亮,但仅当鼠标在Item之上时才会亮子项(多个))。
  • 该框矩形的深灰色是绝对颜色吗? HotLightSelected着色通常由操作系统(==操作系统)处理,尊重用户的主题和颜色选择。这个小工具是否应该忽略这些,或者你的主题使用一些灰色阴影来突出显示?

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

无代码解决方案

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

最后,使用MouseHoverMouseLeave事件来操纵FlatAppearance.BorderSizeFlatAppearance.BorderColor以非常接近问题中的第一张图片:

enter image description here

它们位于滚动Panel中。面板宽度比控件宽一点,以避免水平滚动条。至于按钮,HotLight边框(???)围绕整个控件而不仅仅是文本。不是你想要的图片,但另一方面,除了一些标准属性和一些小事件处理代码(5-6行)之外没有任何其他内容。

Image ListBox

ownerdraw ListBox会让你更接近你想要的东西,但最终这只会使它看起来像ListBox里面有Buttons

在表单上放置一个ListBox ,并设置这些属性:
  - DrawMode = OwnerDrawFixed
  - IntegralHeight = False
  - 物品高度= 64(这是基于第一张图像中图像为60x60的事实)
  - 根据需要将BackColor设置为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

根据评论,您希望根据应用中的其他一些数据自动生成项目。而不是假设该类看起来是什么,并使其易于实现并适用于任何类,这将使用一个接口:

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是您用于跟踪频道的任何课程的我的演示版。只需在您的课程上键入Implements IImageItem,按Enter键,IDE就会添加属性,在将项目添加到您的收藏集之前进行设置。

DrawItem代码

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

同样,目前还不清楚是否有更深的灰色框,用于&#34; FOX&#34;代表SelectedItem , HotLight` / Hover项目,甚至禁用。该代码显示了前两种情况下如何/在哪里打包文本。根据需要修改。

<强>用法

' 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

SelectedValueChannelItem对象设为System.Object,然后将其强制转换为 所有 相关数据,例如作为网址或正在播放文字。

结果:

enter image description here enter image description here

在这种情况下,所选项目有一个SystemColor.Hightlight框(在我的情况下为蓝色),鼠标所在的项目有一个灰色框。在原始图像中,每个项目之间甚至会出现小的2px间隙。这是图像高度略小于ItemHeight使用的结果。

在事物上设置边框会使鼠标无法突出显示。

用户控件

如果你想(几乎)完全控制这些伪按钮的显示方式,你应该建立一个UserControl。使用LabelPictureBox以及正常事件,您可以使用它来完成您想要的任何操作。你可以在大约20分钟内拍一张:

enter image description here

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

1 原因 LV或LB中的项目之间没有间隙是为了让用户更容易选择项目。沟槽或间隙打开了用户可以点击那里并且没有结果的机会。或者根据实现情况,您的代码将使用错误索引崩溃。